interface FixedWindowEntry { type: 'fixed'; count: number; /** Epoch ms when the window resets. */ resetTime: number; } interface SlidingWindowEntry { type: 'sliding'; /** Ascending-sorted epoch ms timestamps within the current window. */ timestamps: number[]; } type StoreEntry = FixedWindowEntry | SlidingWindowEntry; interface BlockRecord { /** Epoch ms when the block expires. */ unblockTime: number; /** Cumulative number of times this key has been blocked. */ blockCount: number; /** Duration (ms) of the most recent block. */ lastBlockDuration: number; /** Full violation history used for progressive escalation. */ blockHistory: ReadonlyArray<{ readonly duration: number; readonly timestamp: number; }>; } /** * Contract every storage backend must satisfy. * * All methods may return values directly (synchronous stores such as MemoryStore) * or wrapped in a Promise (async stores such as Redis or MongoDB adapters). * The middleware awaits every operation, so both forms are transparently supported. * * @example Custom Redis adapter * ```ts * import type { RateLimitStore, StoreEntry, BlockRecord } from 'express-rate-shield/stores' * * class RedisStore implements RateLimitStore { * constructor(private readonly client: RedisClient) {} * async getEntry(key: string) { ... } * async setEntry(key: string, entry: StoreEntry) { ... } * // … * } * ``` */ interface RateLimitStore { getEntry(key: string): Promise | StoreEntry | null; setEntry(key: string, entry: StoreEntry): Promise | void; deleteEntry(key: string): Promise | void; getBlock(key: string): Promise | BlockRecord | null; setBlock(key: string, record: BlockRecord): Promise | void; deleteBlock(key: string): Promise | void; resetKey(key: string): Promise | void; resetAll(): Promise | void; entryCount(): Promise | number; blockedCount(): Promise | number; /** * Called by the built-in cleanup timer every 2 minutes. * Implementations should remove expired trackers and block records. */ cleanup?(windowMs: number): Promise | void; /** Called by RateLimiterInstance.destroy(). */ destroy?(): void | Promise; } export type { BlockRecord as B, FixedWindowEntry as F, RateLimitStore as R, SlidingWindowEntry as S, StoreEntry as a };