import { S as Store, T as Transform } from './types-DKirIBQt.js'; /** * The minimal surface ThrottleKit needs from a Redis client. `ioredis` satisfies this * structurally; any compatible client (same method shapes) works too. */ interface RedisClientLike { evalsha(sha: string, numkeys: number, ...args: Array): Promise; eval(script: string, numkeys: number, ...args: Array): Promise; get(key: string): Promise; del(...keys: string[]): Promise; watch(...keys: string[]): Promise; unwatch(): Promise; multi(): RedisMultiLike; /** * Open a dedicated, isolated connection for one optimistic-concurrency transaction. `WATCH` is * *connection-global* and `EXEC`/`UNWATCH` clear the whole watch set, so concurrent OCC applies that * share one connection cross-contaminate (a lost update, or a spurious abort). The fix runs each * `WATCH`/`MULTI`/`EXEC` on its own connection, released via {@link RedisClientLike.disconnect}. * Optional: when absent, OCC falls back to the shared connection (correct for serialized use, not * for concurrent applies on the same store — e.g. `checkMany`). */ duplicate?(): RedisClientLike; /** Release a connection opened by {@link RedisClientLike.duplicate}. Optional. */ disconnect?(): void | Promise; } /** The subset of a Redis transaction (`MULTI`) used by the optimistic-concurrency fallback. */ interface RedisMultiLike { set(key: string, value: string, mode: "PX", ttlMs: number): RedisMultiLike; exec(): Promise | null>; } interface RedisStoreOptions { /** An `ioredis` (or compatible) client. */ client: RedisClientLike; /** Storage key namespace. */ prefix?: string; /** Use the atomic Lua path for strategies that ship one. Default true. */ useLua?: boolean; /** * Derive `now` from the Redis server clock (`TIME`) inside the script, so node clock skew can't * corrupt shared state. Default true. Set false for deterministic tests that pass an explicit * `now`. (Affects only the absolute `resetAt`; the duration fields stay skew-free either way.) */ useServerTime?: boolean; /** Bounded retries for the optimistic-concurrency fallback (custom strategies). Default 5. */ maxRetries?: number; /** * Floor (ms) on the **physical** key TTL, decoupling Redis GC from the strategy's logical window. * Default 0 (the strategy's own TTL is used verbatim — no extra work). * * A strategy sets the physical `PEXPIRE` to its logical window (e.g. a sliding window's `windowMs`), * which is a *real-time* duration. That is correct under the default `useServerTime: true` (logical * time == the Redis clock that runs the PEXPIRE). But with `useServerTime: false` the logical clock is * a node-supplied `now` decoupled from Redis real time, so a slow real interval between two same-window * writes can let the physical key expire while the logical clock is still inside the window — Redis then * reads a cold window and diverges from an in-memory store on the same logical clock. Set a floor well * above the window so a logically-live key is never reclaimed by real-time GC (lazy logical expiry — * the `start != window_start` reset — still drives every decision, so decisions are unchanged). */ ttlFloorMs?: number; } /** * Distributed store backed by Redis. Built-in strategies run their atomic Lua form in a single * `EVALSHA` round trip (with an `EVAL` fallback on `NOSCRIPT`); strategies without a Lua form fall * back to optimistic concurrency (`WATCH`/`MULTI`/`EXEC`) with bounded retries — correct * everywhere, just not single-round-trip. */ declare class RedisStore implements Store { #private; constructor(options: RedisStoreOptions); apply(key: string, transform: Transform): Promise; reset(key: string): Promise; } export { type RedisClientLike as R, type RedisMultiLike as a, RedisStore as b, type RedisStoreOptions as c };