import type { KVStore } from '../types.js' export interface RedisStoreOptions { readonly keyPrefix?: string } export class RedisStore implements KVStore { private readonly client: { hSet(key: string, ...fields: [string, string][]): Promise hGet(key: string, field: string): Promise del(...keys: string[]): Promise scanIterator(options?: { MATCH?: string; COUNT?: number }): AsyncIterable } private readonly prefix: string constructor( client: RedisStore['client'], options?: RedisStoreOptions, ) { this.client = client this.prefix = options?.keyPrefix ?? '' } private fullKey(key: string): string { return this.prefix ? `${this.prefix}:${key}` : key } async get(key: string): Promise { const value = await this.client.hGet(this.fullKey(key), 'value') return value ?? null } async set(key: string, value: string): Promise { await this.client.hSet(this.fullKey(key), ['value', value]) } async delete(key: string): Promise { await this.client.del(this.fullKey(key)) } async list(): Promise { const pattern = this.prefix ? `${this.prefix}:*` : '*' const keys: string[] = [] for await (const batch of this.client.scanIterator({ MATCH: pattern, COUNT: 100 })) { for (const k of batch) { const stripped = this.prefix ? k.slice(this.prefix.length + 1) : k keys.push(stripped) } } return keys } async clear(): Promise { const keys = await this.list() if (keys.length === 0) return const fullKeys = keys.map((k) => this.fullKey(k)) await this.client.del(...fullKeys) } }