# `redis-store` ## Purpose Provides a Redis-backed `IdempotencyStoreAdapter` with automatic key expiration using Redis native TTL. By accepting a generic `RedisClientAdapter` interface rather than a concrete library, this module integrates with `ioredis`, `redis`, Upstash Redis, or any compatible client without coupling to a specific driver. ## Features - JSON-serialised records stored as Redis string values - Automatic TTL set on every `set()` call based on `record.expiresAt` - Fallback TTL (`defaultTtlSeconds`) for records whose expiry is in the past - Configurable key prefix for namespace isolation (multi-tenant) - `cleanup()` scans expired records server-side for manual purging - Generic `` type parameter for typed payloads ## Usage: Basic Example ```ts import { RedisIdempotencyStore } from "@suaveplan/idempotency/store"; import type { RedisClientAdapter } from "@suaveplan/idempotency/store"; declare const redisAdapter: RedisClientAdapter; // your ioredis wrapper const store = new RedisIdempotencyStore({ client: redisAdapter, prefix: "app:idempotency:", }); ``` ## Usage: Advanced Example ```ts import { RedisIdempotencyStore } from "@suaveplan/idempotency/store"; const store = new RedisIdempotencyStore({ client: redisAdapter, prefix: `tenant:${tenantId}:idempotency:`, defaultTtlSeconds: 3600, // 1 hour fallback }); // Manual cleanup (most records expire via Redis TTL automatically) const removed = await store.cleanup(Date.now()); console.log(`Manually removed ${removed} stale records`); ``` ## API Reference ### `new RedisIdempotencyStore(config: RedisIdempotencyStoreConfig)` | Option | Type | Default | Description | |--------|------|---------|-------------| | `client` | `RedisClientAdapter` | required | Redis client adapter | | `prefix` | `string` | `"idempotency:"` | Key namespace prefix | | `defaultTtlSeconds` | `number` | `86400` | Fallback TTL when expiresAt is past | ### `get(key): Promise | undefined>` Fetches and JSON-parses the record. Returns `undefined` if not found (key expired or never set). ### `set(key, record): Promise` Serialises the record as JSON and stores it with a TTL computed from `record.expiresAt - Date.now()`. ### `delete(key): Promise` Calls `DEL` on the prefixed key; returns `true` if a key was removed. ### `cleanup(beforeTimestamp): Promise` Scans all keys under the prefix, parses each, and deletes those with `expiresAt < beforeTimestamp`. Returns the count removed. ## Implementation Notes - Redis TTL handles the common expiration case automatically; `cleanup` is only needed for records that were not expired by Redis (e.g. records with very long TTLs where early manual deletion is desired). - `scan` is used rather than `KEYS` to avoid blocking the Redis event loop in production. - JSON serialisation assumes records are fully serialisable; `Date` objects should be stored as timestamps (numbers).