/** A rate-limit window after counting one call. */ export interface RateLimitHit { /** Calls counted in the current window, including this one. */ count: number; /** Epoch milliseconds when the window resets. */ resetAt: number; } /** * Where rate-limit counters live. * * Kept separate from `RateLimiter` so the limiter's policy — which key, which window, what counts * as over budget — stays in one place while the counter can move to Redis without touching it. */ export interface RateLimitStore { /** Counts one call against `key` and returns the resulting window state. */ hit(key: string, windowMs: number): Promise | RateLimitHit; /** Resets a key's window. */ reset?(key: string): Promise | void; } /** Process-local counters. The default, and equivalent to the limiter's built-in behavior. */ export declare class MemoryRateLimitStore implements RateLimitStore { private readonly now; private readonly buckets; constructor(now?: () => number); /** Counts one call, starting a new window when the last has ended. */ hit(key: string, windowMs: number): RateLimitHit; /** Resets a key's window. */ reset(key: string): void; /** Resets every window. */ clear(): void; } /** * The Redis commands the rate-limit store needs. * * Structural rather than tied to one client, so `ioredis`, `node-redis`, or a proxy all satisfy it. */ export interface RedisRateLimitLikeClient { /** Increments a key. */ incr(key: string): Promise | number; /** Sets a key's expiry in milliseconds. */ pexpire(key: string, milliseconds: number): Promise | unknown; /** Reads a key's remaining lifetime in milliseconds. */ pttl(key: string): Promise | number; /** Deletes a key, for `reset()`. */ del?(key: string): Promise | unknown; /** Optional atomic primitive. Strongly preferred; see the class note. */ eval?(script: string, numKeys: number, ...args: string[]): Promise | unknown; } /** Options for the Redis rate-limit store. */ export interface RedisRateLimitStoreOptions { /** Key prefix. Defaults to `nexus-ai-pro:ratelimit:`. */ prefix?: string; /** Disables the Lua path even when the client exposes `eval`. */ useEval?: boolean; } /** * Redis-backed counters, so one budget covers every process behind a load balancer. * * An in-memory limiter multiplies the real limit by the number of workers, which is the bug this * exists to remove. Prefer a client exposing `eval`: the increment and the expiry then happen in * one round trip and one atomic step. Without it the store falls back to INCR followed by a * separate PEXPIRE, which leaves a window where a crash between the two calls could leave a key * without a TTL — the fallback re-arms the expiry whenever it sees one missing, so the bucket * recovers rather than blocking the key forever. */ export declare class RedisRateLimitStore implements RateLimitStore { private readonly client; private readonly prefix; private readonly useEval; constructor(client: RedisRateLimitLikeClient, options?: RedisRateLimitStoreOptions); /** Counts one call, in one atomic step when the client has `eval`. */ hit(key: string, windowMs: number): Promise; /** Resets a key's window. Does nothing when the client has no `del`. */ reset(key: string): Promise; }