import { D as Decision, L as Limiter, d as Strategy, S as Store, C as Clock, F as FailMode } from './types-DKirIBQt.cjs'; /** * Standards-compliant rate-limit response headers. * * ThrottleKit emits, per configuration, three flavors of the same decision (see * docs/DESIGN-NOTES.md "IETF RateLimit headers" and THROTTLEKIT.md §15): * * - `draft` — the widely-consumed IETF triple `RateLimit-Limit/Remaining/Reset`, where * `Reset` is **delta-seconds** until replenishment (not a Unix timestamp). * - `structured` — the current draft-ietf-httpapi-ratelimit-headers-11 form using RFC 9651 * Structured Fields: `RateLimit` and `RateLimit-Policy`. * - `legacy` — the long-standing `X-RateLimit-*` triple, whose `Reset` is **epoch seconds**. * * On a denial (`!decision.allowed`) a `Retry-After` header (delta-seconds, rounded up, min 1) is * always added regardless of the emit selection. All values are strings; all time math derives * from the injected `now` so the output is deterministic in tests. */ /** Which header families to emit. Unset flags default to off; the overall default is `{ draft: true }`. */ interface HeaderEmit { /** The IETF triple `RateLimit-Limit/Remaining/Reset` (delta-seconds reset). */ draft?: boolean; /** The RFC 9651 structured `RateLimit` + `RateLimit-Policy` fields (draft-11). */ structured?: boolean; /** The legacy `X-RateLimit-*` triple (epoch-seconds reset). */ legacy?: boolean; } interface BuildRateLimitHeadersOptions { /** * Current time in epoch-ms, used for the delta-seconds (`Reset` / `Retry-After`) math. Optional: * defaults to the system clock. Pass it (e.g. from a {@link ManualClock}) to keep output * deterministic in tests. */ now?: number; /** Policy name surfaced in the structured fields. Defaults to `"default"`. */ policyName?: string; /** Window length in seconds, surfaced as `;w=` of `RateLimit-Policy` when provided. */ windowSeconds?: number; /** Which header families to emit. Defaults to `{ draft: true }`. */ emit?: HeaderEmit; } /** * Build the rate-limit response headers for one {@link Decision}. * * `opts` is optional — called as `buildRateLimitHeaders(decision)` it emits the default `draft` * triple against the system clock. Pass `opts.now` (and the other fields) for deterministic output * or to select header families. * * @returns a plain `Record` ready to be set on a response. Header names use their * canonical casing; values are always strings. */ declare function buildRateLimitHeaders(decision: Decision, opts?: BuildRateLimitHeadersOptions): Record; /** * Proxy-correct, IPv6-aware client IP derivation — a security control, not a convenience. * * Trusting `X-Forwarded-For` blindly is the classic rate-limit bypass: an attacker prepends a * forged hop and rotates the "client" IP at will. This module refuses to do that. The default is * `trustProxy: false` (ignore XFF entirely, use the socket peer). Trust is opt-in and explicit, * either as a hop count (Express "trust proxy" numeric semantics) or a CIDR/IP allowlist of the * proxies you actually run. * * It also aggregates IPv6 to a configurable prefix (`/64` by default), because a single IPv6 * customer controls billions of addresses; limiting per full address is trivially bypassed. * IPv4-mapped IPv6 (`::ffff:1.2.3.4`) collapses to the embedded IPv4. All parsing/masking is * implemented here with no external dependency. See THROTTLEKIT.md §14. */ /** How much of `X-Forwarded-For` to trust, and how aggressively to aggregate IPv6. */ interface TrustProxyConfig { /** * Trust policy for `X-Forwarded-For`: * - `false` (default) — ignore XFF; use `remoteAddr` (the socket peer). * - `number N` — trust `N` hops; the client is the address `N` positions left of the * socket peer in `[...xff, remoteAddr]` (clamped at the leftmost). * - `string[]` — an allowlist of trusted proxy IPs/CIDRs; walking the chain from the * right, the first address NOT in the allowlist is the client. */ trustProxy?: false | number | string[]; /** IPv6 aggregation prefix length in bits. Default 64. Range 0..128. */ ipv6Prefix?: number; } /** Input for {@link clientIp}: the socket peer plus the raw forwarded-for header. */ interface ClientIpInput { /** The socket peer address (e.g. `req.socket.remoteAddress`). */ remoteAddr: string; /** The `X-Forwarded-For` header value: a comma-separated string, an array, or absent. */ xForwardedFor?: string | string[] | undefined; } /** * Derive the proxy-correct, aggregated client IP key from a request's socket peer and * `X-Forwarded-For` header, honoring an explicit trusted-proxy policy. * * @see TrustProxyConfig for the trust semantics. */ declare function clientIp(input: ClientIpInput, config?: TrustProxyConfig): string; /** * Shared adapter core. Every framework binding (Express, fetch/edge, Hono, Next, Fastify, Koa) * needs the same four things: resolve a {@link Limiter} (prebuilt or built inline), pick the clock * for header delta math, build standards headers for a decision, and know the fail policy. That * logic lives here once; each adapter only maps its framework's request/response to these calls. */ /** Options shared by every adapter. */ interface CommonAdapterOptions extends TrustProxyConfig { /** * Store-outage behavior: `"open"` allows, `"closed"` denies. Default `"open"` (availability over * enforcement). **Prefer `"closed"` for security-sensitive limiters** (auth, payments, signup) so a * store outage can't be ridden to bypass the limit. Note the policy applies to *any* error the * limiter throws, not only store outages — pair it with `onError`/`onLimited` for visibility. */ fail?: FailMode; /** Header families to emit, or `false` to emit none. Default `{ draft: true }`. */ emit?: HeaderEmit | false; /** Policy name surfaced in structured headers. Defaults to the strategy name. */ policyName?: string; /** * Edge adapters only: whether to trust the platform-injected `cf-connecting-ip` header as the * client key. Default `true` — correct behind Cloudflare, which **overwrites** any client-supplied * value, and other platforms that inject it. Set `false` when your edge is **not** behind such a * platform, so a client can't spoof the header to rotate rate-limit buckets; the key then comes * from a {@link TrustProxyConfig.trustProxy}-validated `X-Forwarded-For`, or `"anon"` when none is * trusted. (The spoofable rightmost `X-Forwarded-For` is consulted **only** when `trustProxy` is * configured, regardless of this flag.) */ trustClientIpHeader?: boolean; } /** Either pass a prebuilt limiter, or the pieces to build one inline. */ type LimiterOrStrategy = { limiter: Limiter; } | { /** The algorithm to enforce when no `limiter` is supplied. */ strategy: Strategy; /** Where state lives. Defaults to a fresh in-process MemoryStore. */ store?: Store; /** Injected clock. Defaults to the system clock. */ clock?: Clock; /** Key namespace, so one store can back many limiters. */ prefix?: string; }; /** The slice of a Node `IncomingMessage` the Node-style adapters (Express, Fastify, Koa) read. */ interface NodeReqLike { socket?: { remoteAddress?: string | undefined; } | undefined; headers: Record; } export { type BuildRateLimitHeadersOptions as B, type ClientIpInput as C, type HeaderEmit as H, type LimiterOrStrategy as L, type NodeReqLike as N, type TrustProxyConfig as T, type CommonAdapterOptions as a, buildRateLimitHeaders as b, clientIp as c };