@nestjs-redisx/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/api/redis.module.ts","../src/client/application/redis-client.manager.ts","../src/driver/infrastructure/base.driver.ts","../src/interfaces/redis-driver.interface.ts","../src/errors/error-codes.ts","../src/errors/base.error.ts","../src/errors/connection.errors.ts","../src/errors/operation.errors.ts","../src/errors/config.errors.ts","../src/shared/errors/driver.error.ts","../src/driver/infrastructure/ioredis.adapter.ts","../src/types/index.ts","../src/driver/infrastructure/node-redis.adapter.ts","../src/driver/application/driver.factory.ts","../src/shared/constants/injection-tokens.constants.ts","../src/client/domain/interfaces/client-manager.interface.ts","../src/application/redis.providers.ts","../src/application/redis.service.ts","../src/plugin/application/plugin-registry.service.ts","../src/api/decorators/inject-redis.decorator.ts"],"sourcesContent":["/**\n * @nestjs-redisx/core\n *\n * Core module with driver abstraction and plugin system for NestJS RedisX.\n */\n\nexport { RedisModule } from './api/redis.module';\nexport { RedisService } from './application/redis.service';\nexport { InjectRedis } from './api/decorators/inject-redis.decorator';\n\nexport * from './types';\n\nexport * from './interfaces';\n\nexport * from './driver';\n\nexport * from './client';\n\nexport type { IRedisXPlugin, IPluginContext } from './plugin/domain/interfaces';\nexport { PluginRegistryService } from './plugin/application/plugin-registry.service';\n\nexport * from './errors';\n\nexport * from './shared/errors';\nexport * from './shared/constants';\nexport * from './shared/types';\n\nexport { REDIS_MODULE_OPTIONS, REDIS_CLIENT, REDIS_CLIENTS_MAP, REDIS_DRIVER, REDISX_CONFIG, CLIENT_MANAGER, PLUGIN_REGISTRY, REGISTERED_PLUGINS, REDISX_LOGGER, getClientToken, getDriverToken, DEFAULT_CLIENT_NAME } from './shared/constants';\n","import { Module, DynamicModule, Provider, Type } from '@nestjs/common';\nimport { DiscoveryModule } from '@nestjs/core';\n\nimport { createRedisProviders, createAsyncProviders } from '../application/redis.providers';\nimport { RedisService } from '../application/redis.service';\nimport { PluginRegistryService } from '../plugin/application/plugin-registry.service';\nimport { CLIENT_MANAGER, REGISTERED_PLUGINS } from '../shared/constants';\nimport { IRedisModuleOptions, IRedisModuleAsyncOptions } from '../types';\n\n/**\n * Redis module for NestJS.\n *\n * Global module that provides Redis client(s) with driver abstraction,\n * health monitoring, automatic reconnection, and plugin system.\n *\n * Module is always global - no need to import in feature modules.\n *\n * @example\n * **Synchronous configuration:**\n * ```typescript\n * @Module({\n * imports: [\n * RedisModule.forRoot({\n * clients: {\n * type: 'single',\n * host: 'localhost',\n * port: 6379,\n * },\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n *\n * @example\n * **Asynchronous configuration with factory:**\n * ```typescript\n * @Module({\n * imports: [\n * RedisModule.forRootAsync({\n * useFactory: (config: ConfigService) => ({\n * clients: {\n * type: 'single',\n * host: config.get('REDIS_HOST'),\n * port: config.get('REDIS_PORT'),\n * },\n * }),\n * inject: [ConfigService],\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n *\n * @example\n * **With plugins:**\n * ```typescript\n * @Module({\n * imports: [\n * RedisModule.forRoot({\n * clients: { type: 'single', host: 'localhost', port: 6379 },\n * plugins: [new CachePlugin(), new LocksPlugin()],\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n@Module({})\nexport class RedisModule {\n /**\n * Configures Redis module synchronously.\n *\n * Creates a global module with Redis clients and plugins.\n * All providers are available for injection everywhere.\n *\n * @param options - Redis module options\n * @returns Dynamic module\n *\n * @example\n * ```typescript\n * RedisModule.forRoot({\n * clients: {\n * type: 'single',\n * host: 'localhost',\n * port: 6379,\n * },\n * plugins: [new CachePlugin(), new LocksPlugin()],\n * })\n * ```\n */\n static forRoot(options: IRedisModuleOptions): DynamicModule {\n const providers = createRedisProviders(options);\n const pluginProviders: Provider[] = [];\n const pluginExports: Array<string | symbol | Provider> = [];\n const pluginControllers: Type[] = [];\n\n // Register plugin providers and collect exports and controllers\n if (options.plugins && options.plugins.length > 0) {\n options.plugins.forEach((plugin) => {\n if (plugin.getProviders) {\n const providers = plugin.getProviders();\n if (Array.isArray(providers)) {\n pluginProviders.push(...providers);\n }\n }\n if (plugin.getExports) {\n const exports = plugin.getExports();\n if (Array.isArray(exports)) {\n pluginExports.push(...exports);\n }\n }\n if (plugin.getControllers) {\n const controllers = plugin.getControllers();\n if (Array.isArray(controllers)) {\n pluginControllers.push(...controllers);\n }\n }\n });\n }\n\n const plugins = options.plugins || [];\n\n return {\n module: RedisModule,\n global: true,\n imports: [DiscoveryModule], // Required for plugins that scan providers (e.g., CachePlugin)\n providers: [...providers, ...pluginProviders, { provide: REGISTERED_PLUGINS, useValue: plugins }, PluginRegistryService, RedisService],\n controllers: pluginControllers,\n exports: [CLIENT_MANAGER, RedisService, ...pluginExports],\n };\n }\n\n /**\n * Configures Redis module asynchronously.\n *\n * Allows configuration to be loaded asynchronously using factory function,\n * existing provider, or class-based factory. Supports plugins.\n *\n * Note: Plugins must be provided in the async options (outside useFactory),\n * not in the factory result. This is a standard NestJS pattern — plugins\n * must be available at module construction time for NestJS DI.\n *\n * @param options - Async Redis module options\n * @returns Dynamic module\n *\n * @example\n * **Using factory with plugins:**\n * ```typescript\n * RedisModule.forRootAsync({\n * plugins: [new CachePlugin(), new LocksPlugin()],\n * useFactory: (config: ConfigService) => ({\n * clients: {\n * type: 'single',\n * host: config.get('REDIS_HOST'),\n * port: config.get('REDIS_PORT'),\n * },\n * }),\n * inject: [ConfigService],\n * })\n * ```\n *\n * @example\n * **Using class:**\n * ```typescript\n * @Injectable()\n * class RedisConfigService implements IRedisModuleOptionsFactory {\n * createRedisModuleOptions(): IRedisModuleOptions {\n * return {\n * clients: { type: 'single', host: 'localhost', port: 6379 },\n * };\n * }\n * }\n *\n * RedisModule.forRootAsync({\n * plugins: [new CachePlugin()],\n * useClass: RedisConfigService,\n * })\n * ```\n */\n static forRootAsync(options: IRedisModuleAsyncOptions): DynamicModule {\n const baseProviders = createAsyncProviders(options);\n const imports = options.imports || [];\n\n // Extract plugin providers from options (provided outside useFactory)\n // This is the standard NestJS pattern - plugins are statically available,\n // similar to how @nestjs/typeorm handles entities or @nestjs/graphql handles resolvers\n const plugins = options.plugins || [];\n const pluginProviders: Provider[] = [];\n const pluginExports: Array<string | symbol | Provider> = [];\n const pluginControllers: Type[] = [];\n\n // Register plugin providers and collect exports and controllers\n if (plugins.length > 0) {\n plugins.forEach((plugin) => {\n if (plugin.getProviders) {\n const providers = plugin.getProviders();\n if (Array.isArray(providers)) {\n pluginProviders.push(...providers);\n }\n }\n if (plugin.getExports) {\n const exports = plugin.getExports();\n if (Array.isArray(exports)) {\n pluginExports.push(...exports);\n }\n }\n if (plugin.getControllers) {\n const controllers = plugin.getControllers();\n if (Array.isArray(controllers)) {\n pluginControllers.push(...controllers);\n }\n }\n });\n }\n\n return {\n module: RedisModule,\n global: true,\n imports: [DiscoveryModule, ...imports], // DiscoveryModule required for plugins\n providers: [...baseProviders, ...pluginProviders, { provide: REGISTERED_PLUGINS, useValue: plugins }, PluginRegistryService, RedisService],\n controllers: pluginControllers,\n exports: [CLIENT_MANAGER, RedisService, ...pluginExports],\n };\n }\n}\n","import EventEmitter from 'events';\n\nimport { Injectable, Logger, OnModuleDestroy } from '@nestjs/common';\n\nimport { createDriver } from '../../driver';\nimport { DriverType } from '../../types';\nimport { IRedisDriver, DriverEvent } from '../../interfaces';\nimport { DEFAULT_CLIENT_NAME } from '../../shared/constants';\nimport { RedisXError, ErrorCode } from '../../errors';\nimport { ConnectionConfig, IClientMetadata, ConnectionStatus } from '../../types';\nimport { IRedisDriverManager, ManagerEvent, IManagerEventData, ManagerEventHandler } from '../domain/interfaces/client-manager.interface';\nimport { IHealthStatus, IConnectionStats, IClientStats, IReconnectionOptions } from '../domain/types/health.types';\n\n/**\n * Client wrapper with metadata and statistics.\n */\ninterface IManagedClient {\n /**\n * Client name.\n */\n name: string;\n\n /**\n * Redis driver instance.\n */\n driver: IRedisDriver;\n\n /**\n * Connection configuration.\n */\n config: ConnectionConfig;\n\n /**\n * Client metadata.\n */\n metadata: IClientMetadata;\n\n /**\n * Reconnection options.\n */\n reconnectionOptions: Required<IReconnectionOptions>;\n\n /**\n * Current connection status.\n */\n status: ConnectionStatus;\n\n /**\n * Statistics.\n */\n stats: IClientStats;\n\n /**\n * Reconnection state.\n */\n reconnection: {\n attempts: number;\n nextDelay: number;\n timer?: NodeJS.Timeout;\n };\n\n /**\n * Whether client has been connected at least once.\n */\n everConnected: boolean;\n\n /**\n * Last error if any.\n */\n lastError: Error | null;\n}\n\n/**\n * Redis Client Manager.\n *\n * Manages multiple Redis client connections with:\n * - Lazy connection (connect on first use)\n * - Automatic reconnection with exponential backoff\n * - Health monitoring and statistics\n * - Graceful shutdown\n * - Event notifications\n *\n * @example\n * ```typescript\n * @Injectable()\n * export class AppService {\n * constructor(\n * @Inject(CLIENT_MANAGER) private readonly clientManager: RedisClientManager\n * ) {}\n *\n * async cacheValue(key: string, value: string): Promise<void> {\n * const client = await this.clientManager.getClient('cache');\n * await client.set(key, value, { ex: 3600 });\n * }\n * }\n * ```\n */\n@Injectable()\nexport class RedisClientManager implements IRedisDriverManager, OnModuleDestroy {\n private readonly logger = new Logger(RedisClientManager.name);\n\n /**\n * Registered clients.\n */\n private readonly clients = new Map<string, IManagedClient>();\n\n /**\n * Event emitter for client events.\n */\n private readonly eventEmitter = new EventEmitter();\n\n /**\n * Whether manager is shutting down.\n */\n private isShuttingDown = false;\n\n /**\n * Default reconnection options.\n */\n private readonly defaultIReconnectionOptions: Required<IReconnectionOptions> = {\n maxAttempts: Infinity,\n initialDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n enableJitter: true,\n };\n\n /**\n * Default driver type.\n */\n private readonly defaultDriverType: DriverType = 'ioredis';\n\n /**\n * Gets Redis client by name with lazy connection.\n */\n async getClient(name: string = DEFAULT_CLIENT_NAME): Promise<IRedisDriver> {\n if (this.isShuttingDown) {\n throw new RedisXError('Client manager is shutting down', ErrorCode.CONN_FAILED, undefined, { clientName: name });\n }\n\n const managedClient = this.clients.get(name);\n\n if (!managedClient) {\n throw new RedisXError(`Client \"${name}\" not found. Available clients: ${Array.from(this.clients.keys()).join(', ')}`, ErrorCode.CFG_INVALID, undefined, { clientName: name });\n }\n\n // Lazy connection on first use\n if (!managedClient.driver.isConnected() && !managedClient.everConnected) {\n await this.connectClient(managedClient);\n }\n\n return managedClient.driver;\n }\n\n /**\n * Creates and registers a new Redis client.\n */\n // eslint-disable-next-line @typescript-eslint/require-await\n async createClient(\n name: string,\n config: ConnectionConfig,\n options?: {\n reconnection?: IReconnectionOptions;\n metadata?: Partial<IClientMetadata>;\n driverType?: DriverType;\n },\n ): Promise<IRedisDriver> {\n if (this.clients.has(name)) {\n throw new RedisXError(`Client \"${name}\" already exists`, ErrorCode.CFG_INVALID, undefined, { clientName: name });\n }\n\n // Create driver\n const driver = createDriver(config, {\n type: options?.driverType ?? this.defaultDriverType,\n enableLogging: false,\n });\n\n // Setup reconnection options\n const reconnectionOptions: Required<IReconnectionOptions> = {\n ...this.defaultIReconnectionOptions,\n ...options?.reconnection,\n };\n\n // Create metadata\n const metadata: IClientMetadata = {\n name,\n config,\n status: ConnectionStatus.DISCONNECTED,\n reconnectAttempts: 0,\n ...(options?.metadata || {}),\n };\n\n // Create managed client\n const managedClient: IManagedClient = {\n name,\n driver,\n config,\n metadata,\n reconnectionOptions,\n status: ConnectionStatus.DISCONNECTED,\n stats: this.createInitialStats(name),\n reconnection: {\n attempts: 0,\n nextDelay: reconnectionOptions.initialDelay,\n },\n everConnected: false,\n lastError: null,\n };\n\n // Setup event handlers\n this.setupDriverEventHandlers(managedClient);\n\n // Register client\n this.clients.set(name, managedClient);\n\n // Emit event\n this.emitEvent(ManagerEvent.CREATED, {\n name,\n timestamp: new Date(),\n });\n\n return driver;\n }\n\n /**\n * Checks if client exists.\n */\n hasClient(name: string): boolean {\n return this.clients.has(name);\n }\n\n /**\n * Gets all registered client names.\n */\n getClientNames(): string[] {\n return Array.from(this.clients.keys());\n }\n\n /**\n * Closes specific client connection.\n */\n async closeClient(name: string): Promise<void> {\n const managedClient = this.clients.get(name);\n\n if (!managedClient) {\n throw new RedisXError(`Client \"${name}\" not found`, ErrorCode.CFG_INVALID, undefined, { clientName: name });\n }\n\n await this.disconnectClient(managedClient);\n\n this.clients.delete(name);\n\n this.emitEvent(ManagerEvent.REMOVED, {\n name,\n timestamp: new Date(),\n });\n }\n\n /**\n * Closes all client connections.\n */\n async closeAll(): Promise<void> {\n this.isShuttingDown = true;\n\n const closePromises = Array.from(this.clients.values()).map((client) =>\n this.disconnectClient(client).catch((error) => {\n // Log error but don't fail\n this.logger.error(`Error closing client ${client.name}:`, error);\n }),\n );\n\n await Promise.all(closePromises);\n\n this.clients.clear();\n }\n\n /**\n * Performs health check on client(s).\n */\n async healthCheck(name?: string): Promise<IHealthStatus | IHealthStatus[]> {\n if (name) {\n // Check single client\n const managedClient = this.clients.get(name);\n\n if (!managedClient) {\n throw new RedisXError(`Client \"${name}\" not found`, ErrorCode.CFG_INVALID, undefined, { clientName: name });\n }\n\n return this.checkClientHealth(managedClient);\n } else {\n // Check all clients\n const healthChecks = await Promise.all(Array.from(this.clients.values()).map((client) => this.checkClientHealth(client)));\n\n return healthChecks;\n }\n }\n\n /**\n * Gets connection statistics.\n */\n getStats(): IConnectionStats {\n const stats: IConnectionStats = {\n totalClients: this.clients.size,\n connectedClients: 0,\n disconnectedClients: 0,\n errorClients: 0,\n clients: {},\n collectedAt: new Date(),\n };\n\n for (const [name, client] of this.clients) {\n stats.clients[name] = { ...client.stats };\n\n if (client.status === ConnectionStatus.CONNECTED) {\n stats.connectedClients++;\n } else if (client.status === ConnectionStatus.ERROR) {\n stats.errorClients++;\n } else {\n stats.disconnectedClients++;\n }\n }\n\n return stats;\n }\n\n /**\n * Gets client metadata.\n */\n getMetadata(name: string): IClientMetadata {\n const managedClient = this.clients.get(name);\n\n if (!managedClient) {\n throw new RedisXError(`Client \"${name}\" not found`, ErrorCode.CFG_INVALID, undefined, { clientName: name });\n }\n\n return { ...managedClient.metadata };\n }\n\n /**\n * Updates client metadata.\n */\n updateMetadata(name: string, metadata: Partial<IClientMetadata>): void {\n const managedClient = this.clients.get(name);\n\n if (!managedClient) {\n throw new RedisXError(`Client \"${name}\" not found`, ErrorCode.CFG_INVALID, undefined, { clientName: name });\n }\n\n Object.assign(managedClient.metadata, metadata);\n }\n\n /**\n * Registers event listener.\n */\n on(event: ManagerEvent, handler: ManagerEventHandler): void {\n this.eventEmitter.on(event, handler);\n }\n\n /**\n * Unregisters event listener.\n */\n off(event: ManagerEvent, handler: ManagerEventHandler): void {\n this.eventEmitter.off(event, handler);\n }\n\n /**\n * NestJS lifecycle hook - cleanup on module destroy.\n */\n async onModuleDestroy(): Promise<void> {\n await this.closeAll();\n }\n\n /**\n * Connects client with error handling.\n */\n private async connectClient(managedClient: IManagedClient): Promise<void> {\n try {\n managedClient.status = ConnectionStatus.CONNECTING;\n\n await managedClient.driver.connect();\n\n managedClient.status = ConnectionStatus.CONNECTED;\n managedClient.everConnected = true;\n managedClient.stats.connectedAt = new Date();\n managedClient.reconnection.attempts = 0;\n managedClient.reconnection.nextDelay = managedClient.reconnectionOptions.initialDelay!;\n managedClient.lastError = null;\n\n this.emitEvent(ManagerEvent.CONNECTED, {\n name: managedClient.name,\n timestamp: new Date(),\n });\n } catch (error) {\n managedClient.status = ConnectionStatus.ERROR;\n managedClient.stats.errors++;\n managedClient.lastError = error as Error;\n\n this.emitEvent(ManagerEvent.ERROR, {\n name: managedClient.name,\n timestamp: new Date(),\n error: error as Error,\n });\n\n throw error;\n }\n }\n\n /**\n * Disconnects client gracefully.\n */\n private async disconnectClient(managedClient: IManagedClient): Promise<void> {\n // Clear reconnection timer\n if (managedClient.reconnection.timer) {\n clearTimeout(managedClient.reconnection.timer);\n managedClient.reconnection.timer = undefined;\n }\n\n // Disconnect driver\n if (managedClient.driver.isConnected()) {\n try {\n await managedClient.driver.disconnect();\n } catch (error) {\n // Ignore disconnect errors during shutdown\n this.logger.error(`Error disconnecting client ${managedClient.name}:`, error);\n }\n }\n\n managedClient.status = ConnectionStatus.DISCONNECTED;\n\n this.emitEvent(ManagerEvent.DISCONNECTED, {\n name: managedClient.name,\n timestamp: new Date(),\n });\n }\n\n /**\n * Sets up driver event handlers for reconnection.\n */\n private setupDriverEventHandlers(managedClient: IManagedClient): void {\n const { driver, name } = managedClient;\n\n // Handle errors\n driver.on(DriverEvent.ERROR, (error?: unknown) => {\n managedClient.stats.errors++;\n managedClient.lastError = error as Error;\n\n if (managedClient.status === ConnectionStatus.CONNECTED) {\n // Connection lost, trigger reconnection\n this.scheduleReconnection(managedClient);\n }\n });\n\n // Handle close/end events\n driver.on(DriverEvent.CLOSE, () => {\n if (managedClient.status === ConnectionStatus.CONNECTED && !this.isShuttingDown) {\n managedClient.status = ConnectionStatus.DISCONNECTED;\n this.scheduleReconnection(managedClient);\n }\n });\n\n driver.on(DriverEvent.END, () => {\n if (managedClient.status === ConnectionStatus.CONNECTED && !this.isShuttingDown) {\n managedClient.status = ConnectionStatus.DISCONNECTED;\n this.scheduleReconnection(managedClient);\n }\n });\n\n // Handle ready event\n driver.on(DriverEvent.READY, () => {\n if (managedClient.status === ConnectionStatus.RECONNECTING) {\n managedClient.status = ConnectionStatus.CONNECTED;\n managedClient.reconnection.attempts = 0;\n managedClient.reconnection.nextDelay = managedClient.reconnectionOptions.initialDelay!;\n\n this.emitEvent(ManagerEvent.CONNECTED, {\n name,\n timestamp: new Date(),\n });\n }\n });\n }\n\n /**\n * Schedules reconnection with exponential backoff.\n */\n private scheduleReconnection(managedClient: IManagedClient): void {\n const { reconnectionOptions, reconnection } = managedClient;\n\n // Check if we've exceeded max attempts\n if (reconnection.attempts >= reconnectionOptions.maxAttempts) {\n managedClient.status = ConnectionStatus.ERROR;\n this.emitEvent(ManagerEvent.ERROR, {\n name: managedClient.name,\n timestamp: new Date(),\n error: new Error(`Max reconnection attempts (${reconnectionOptions.maxAttempts}) exceeded`),\n });\n return;\n }\n\n // Clear existing timer\n if (reconnection.timer) {\n clearTimeout(reconnection.timer);\n }\n\n // Calculate delay with exponential backoff\n let delay = reconnection.nextDelay;\n\n // Add jitter if enabled\n if (reconnectionOptions.enableJitter) {\n delay = delay * (0.5 + Math.random() * 0.5);\n }\n\n // Cap at max delay\n delay = Math.min(delay, reconnectionOptions.maxDelay);\n\n managedClient.status = ConnectionStatus.RECONNECTING;\n reconnection.attempts++;\n\n this.emitEvent(ManagerEvent.RECONNECTING, {\n name: managedClient.name,\n timestamp: new Date(),\n metadata: {\n attempt: reconnection.attempts,\n delay,\n },\n });\n\n // Schedule reconnection\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n reconnection.timer = setTimeout(async () => {\n try {\n await managedClient.driver.connect();\n\n managedClient.status = ConnectionStatus.CONNECTED;\n managedClient.stats.reconnections++;\n reconnection.attempts = 0;\n reconnection.nextDelay = reconnectionOptions.initialDelay!;\n\n this.emitEvent(ManagerEvent.CONNECTED, {\n name: managedClient.name,\n timestamp: new Date(),\n });\n } catch {\n // Reconnection failed, calculate next delay\n reconnection.nextDelay = Math.min(reconnection.nextDelay * reconnectionOptions.backoffMultiplier, reconnectionOptions.maxDelay);\n\n // Schedule next attempt\n this.scheduleReconnection(managedClient);\n }\n }, delay);\n }\n\n /**\n * Performs health check on single client.\n */\n private async checkClientHealth(managedClient: IManagedClient): Promise<IHealthStatus> {\n const startTime = Date.now();\n let latency: number | null = null;\n let healthy = false;\n\n try {\n if (managedClient.driver.isConnected()) {\n await managedClient.driver.ping();\n latency = Date.now() - startTime;\n healthy = true;\n\n // Update stats\n managedClient.stats.lastActivityAt = new Date();\n this.updateLatencyStats(managedClient, latency);\n }\n } catch (error) {\n managedClient.stats.errors++;\n managedClient.lastError = error as Error;\n }\n\n return {\n name: managedClient.name,\n healthy,\n status: managedClient.status,\n latency,\n lastError: managedClient.lastError?.message ?? null,\n lastCheckAt: new Date(),\n metadata: {\n driverType: this.defaultDriverType as string,\n connectionType: managedClient.config.type as string,\n reconnectAttempts: managedClient.reconnection.attempts,\n uptime: this.calculateUptime(managedClient),\n },\n };\n }\n\n /**\n * Creates initial statistics for new client.\n */\n private createInitialStats(name: string): IClientStats {\n return {\n name,\n status: ConnectionStatus.DISCONNECTED,\n commandsExecuted: 0,\n errors: 0,\n reconnections: 0,\n averageLatency: 0,\n peakLatency: 0,\n lastActivityAt: null,\n connectedAt: null,\n uptime: 0,\n };\n }\n\n /**\n * Updates latency statistics.\n */\n private updateLatencyStats(managedClient: IManagedClient, latency: number): void {\n const stats = managedClient.stats;\n\n // Update peak\n if (latency > stats.peakLatency) {\n stats.peakLatency = latency;\n }\n\n // Update average (simple moving average)\n if (stats.commandsExecuted === 0) {\n stats.averageLatency = latency;\n } else {\n stats.averageLatency = (stats.averageLatency * stats.commandsExecuted + latency) / (stats.commandsExecuted + 1);\n }\n\n stats.commandsExecuted++;\n }\n\n /**\n * Calculates client uptime in milliseconds.\n */\n private calculateUptime(managedClient: IManagedClient): number {\n if (!managedClient.stats.connectedAt) {\n return 0;\n }\n\n if (managedClient.status !== ConnectionStatus.CONNECTED) {\n return 0;\n }\n\n return Date.now() - managedClient.stats.connectedAt.getTime();\n }\n\n /**\n * Emits manager event.\n */\n private emitEvent(event: ManagerEvent, data: IManagerEventData): void {\n this.eventEmitter.emit(event, data);\n }\n}\n","import EventEmitter from 'events';\n\nimport { Logger } from '@nestjs/common';\nimport { IRedisDriver, IPipeline, IMulti, ISetOptions, IScanOptions, IStreamAddOptions, IStreamReadOptions, IStreamReadGroupOptions, IStreamEntry, StreamReadResult, IStreamInfo, IStreamPendingInfo, IStreamPendingEntry, DriverEvent, DriverEventHandler, ICopyOptions, IRestoreOptions, ILposOptions, IZStoreOptions, IZRangeByScoreOptions, GeoUnit, IGeoSearchOptions, IGeoSearchResult } from '../../interfaces';\nimport { DriverError, TimeoutError } from '../../shared/errors';\nimport { ConnectionConfig } from '../../types';\n\n/**\n * Base Redis driver implementation.\n *\n * Provides common functionality for all driver implementations.\n * Subclasses must implement abstract methods for specific Redis clients.\n *\n * Features:\n * - Event handling\n * - Connection state management\n * - Error wrapping\n * - Optional operation logging\n *\n * @abstract\n */\nexport abstract class BaseRedisDriver implements IRedisDriver {\n protected readonly eventEmitter: EventEmitter;\n protected readonly logger: Logger;\n protected connected = false;\n protected connecting = false;\n protected readonly enableLogging: boolean;\n\n constructor(\n protected readonly config: ConnectionConfig,\n options?: {\n enableLogging?: boolean;\n },\n ) {\n this.eventEmitter = new EventEmitter();\n this.logger = new Logger(this.constructor.name);\n this.enableLogging = options?.enableLogging ?? false;\n }\n\n /**\n * Establishes connection to Redis.\n * Must set `this.connected = true` on success.\n */\n protected abstract doConnect(): Promise<void>;\n\n /**\n * Closes connection to Redis.\n * Must set `this.connected = false` on completion.\n */\n protected abstract doDisconnect(): Promise<void>;\n\n /**\n * Executes raw Redis command.\n * @param command - Command name\n * @param args - Command arguments\n */\n protected abstract executeCommand(command: string, ...args: unknown[]): Promise<unknown>;\n\n /**\n * Creates pipeline instance.\n */\n protected abstract createPipeline(): IPipeline;\n\n /**\n * Creates multi/exec transaction instance.\n */\n protected abstract createMulti(): IMulti;\n\n async connect(): Promise<void> {\n if (this.connected) {\n this.log('Already connected, skipping connect');\n return;\n }\n\n if (this.connecting) {\n this.log('Connection in progress, waiting...');\n return new Promise((resolve, reject) => {\n const onConnect = (): void => {\n cleanup();\n resolve();\n };\n const onError = (error?: unknown): void => {\n cleanup();\n reject(error);\n };\n const cleanup = (): void => {\n this.off(DriverEvent.READY, onConnect);\n this.off(DriverEvent.ERROR, onError);\n };\n this.once(DriverEvent.READY, onConnect);\n this.once(DriverEvent.ERROR, onError);\n });\n }\n\n try {\n this.connecting = true;\n this.log('Connecting to Redis...');\n this.emit(DriverEvent.CONNECT);\n\n await this.doConnect();\n\n this.connected = true;\n this.connecting = false;\n this.log('Connected to Redis');\n this.emit(DriverEvent.READY);\n } catch (error) {\n this.connecting = false;\n this.log('Connection failed', error);\n this.emit(DriverEvent.ERROR, error);\n throw error;\n }\n }\n\n async disconnect(): Promise<void> {\n if (!this.connected) {\n this.log('Not connected, skipping disconnect');\n return;\n }\n\n try {\n this.log('Disconnecting from Redis...');\n this.emit(DriverEvent.DISCONNECT);\n\n await this.doDisconnect();\n\n this.connected = false;\n this.log('Disconnected from Redis');\n this.emit(DriverEvent.CLOSE);\n } catch (error) {\n this.log('Disconnect failed', error);\n this.emit(DriverEvent.ERROR, error);\n throw error;\n }\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n async ping(message?: string): Promise<string> {\n this.assertConnected();\n const result = message ? await this.executeCommand('PING', message) : await this.executeCommand('PING');\n return String(result);\n }\n\n async select(db: number): Promise<void> {\n this.assertConnected();\n await this.executeCommand('SELECT', db);\n }\n\n async get(key: string): Promise<string | null> {\n this.assertConnected();\n const result = await this.executeCommand('GET', key);\n return result === null || result === undefined ? null : String(result);\n }\n\n async set(key: string, value: string, options?: ISetOptions): Promise<'OK' | null> {\n this.assertConnected();\n const args: unknown[] = [key, value];\n\n if (options) {\n if (options.ex !== undefined) {\n args.push('EX', options.ex);\n }\n if (options.px !== undefined) {\n args.push('PX', options.px);\n }\n if (options.exat !== undefined) {\n args.push('EXAT', options.exat);\n }\n if (options.pxat !== undefined) {\n args.push('PXAT', options.pxat);\n }\n if (options.nx) {\n args.push('NX');\n }\n if (options.xx) {\n args.push('XX');\n }\n if (options.get) {\n args.push('GET');\n }\n if (options.keepttl) {\n args.push('KEEPTTL');\n }\n }\n\n const result = await this.executeCommand('SET', ...args);\n return result === 'OK' ? 'OK' : null;\n }\n\n async mget(...keys: string[]): Promise<Array<string | null>> {\n this.assertConnected();\n const result = await this.executeCommand('MGET', ...keys);\n return (result as unknown[]).map((v) => (v === null || v === undefined ? null : String(v)));\n }\n\n async mset(data: Record<string, string>): Promise<'OK'> {\n this.assertConnected();\n const args: string[] = [];\n for (const [key, value] of Object.entries(data)) {\n args.push(key, value);\n }\n await this.executeCommand('MSET', ...args);\n return 'OK';\n }\n\n async setnx(key: string, value: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SETNX', key, value);\n return Number(result);\n }\n\n async setex(key: string, seconds: number, value: string): Promise<'OK'> {\n this.assertConnected();\n await this.executeCommand('SETEX', key, seconds, value);\n return 'OK';\n }\n\n async getdel(key: string): Promise<string | null> {\n this.assertConnected();\n const result = await this.executeCommand('GETDEL', key);\n return result === null || result === undefined ? null : String(result);\n }\n\n async getex(key: string, options: { ex?: number; px?: number }): Promise<string | null> {\n this.assertConnected();\n const args: unknown[] = [key];\n if (options.ex !== undefined) {\n args.push('EX', options.ex);\n }\n if (options.px !== undefined) {\n args.push('PX', options.px);\n }\n const result = await this.executeCommand('GETEX', ...args);\n return result === null || result === undefined ? null : String(result);\n }\n\n async incr(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('INCR', key);\n return Number(result);\n }\n\n async incrby(key: string, increment: number): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('INCRBY', key, increment);\n return Number(result);\n }\n\n async decr(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('DECR', key);\n return Number(result);\n }\n\n async decrby(key: string, decrement: number): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('DECRBY', key, decrement);\n return Number(result);\n }\n\n async append(key: string, value: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('APPEND', key, value);\n return Number(result);\n }\n\n async strlen(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('STRLEN', key);\n return Number(result);\n }\n\n async incrbyfloat(key: string, increment: number): Promise<string> {\n this.assertConnected();\n const result = await this.executeCommand('INCRBYFLOAT', key, increment);\n return String(result);\n }\n\n async getrange(key: string, start: number, end: number): Promise<string> {\n this.assertConnected();\n const result = await this.executeCommand('GETRANGE', key, start, end);\n return String(result);\n }\n\n async setrange(key: string, offset: number, value: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SETRANGE', key, offset, value);\n return Number(result);\n }\n\n async msetnx(data: Record<string, string>): Promise<number> {\n this.assertConnected();\n const args: string[] = [];\n for (const [key, value] of Object.entries(data)) {\n args.push(key, value);\n }\n const result = await this.executeCommand('MSETNX', ...args);\n return Number(result);\n }\n\n async del(...keys: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('DEL', ...keys);\n return Number(result);\n }\n\n async exists(...keys: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('EXISTS', ...keys);\n return Number(result);\n }\n\n async expire(key: string, seconds: number): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('EXPIRE', key, seconds);\n return Number(result);\n }\n\n async pexpire(key: string, milliseconds: number): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('PEXPIRE', key, milliseconds);\n return Number(result);\n }\n\n async expireat(key: string, timestamp: number): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('EXPIREAT', key, timestamp);\n return Number(result);\n }\n\n async ttl(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('TTL', key);\n return Number(result);\n }\n\n async pttl(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('PTTL', key);\n return Number(result);\n }\n\n async persist(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('PERSIST', key);\n return Number(result);\n }\n\n async rename(key: string, newKey: string): Promise<'OK'> {\n this.assertConnected();\n await this.executeCommand('RENAME', key, newKey);\n return 'OK';\n }\n\n async renamenx(key: string, newKey: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('RENAMENX', key, newKey);\n return Number(result);\n }\n\n async type(key: string): Promise<string> {\n this.assertConnected();\n const result = await this.executeCommand('TYPE', key);\n return String(result);\n }\n\n async scan(cursor: number, options?: IScanOptions): Promise<[string, string[]]> {\n this.assertConnected();\n const args: unknown[] = [cursor];\n if (options?.match) {\n args.push('MATCH', options.match);\n }\n if (options?.count) {\n args.push('COUNT', options.count);\n }\n if (options?.type) {\n args.push('TYPE', options.type);\n }\n const result = await this.executeCommand('SCAN', ...args);\n const [newCursor, keys] = result as [string, string[]];\n return [String(newCursor), keys];\n }\n\n async unlink(...keys: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('UNLINK', ...keys);\n return Number(result);\n }\n\n async copy(source: string, destination: string, options?: ICopyOptions): Promise<number> {\n this.assertConnected();\n const args: unknown[] = [source, destination];\n if (options?.db !== undefined) {\n args.push('DB', options.db);\n }\n if (options?.replace) {\n args.push('REPLACE');\n }\n const result = await this.executeCommand('COPY', ...args);\n return Number(result);\n }\n\n async keys(pattern: string): Promise<string[]> {\n this.assertConnected();\n const result = await this.executeCommand('KEYS', pattern);\n return result as string[];\n }\n\n async touch(...keys: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('TOUCH', ...keys);\n return Number(result);\n }\n\n async object(subcommand: 'ENCODING' | 'FREQ' | 'IDLETIME' | 'REFCOUNT', key: string): Promise<string | number | null> {\n this.assertConnected();\n const result = await this.executeCommand('OBJECT', subcommand, key);\n if (result === null) return null;\n if (subcommand === 'ENCODING') return String(result);\n return Number(result);\n }\n\n async dump(key: string): Promise<string | null> {\n this.assertConnected();\n const result = await this.executeCommand('DUMP', key);\n return result === null ? null : String(result);\n }\n\n async restore(key: string, ttl: number, serializedValue: string, options?: IRestoreOptions): Promise<'OK'> {\n this.assertConnected();\n const args: unknown[] = [key, ttl, serializedValue];\n if (options?.replace) {\n args.push('REPLACE');\n }\n if (options?.absttl) {\n args.push('ABSTTL');\n }\n if (options?.idletime !== undefined) {\n args.push('IDLETIME', options.idletime);\n }\n if (options?.freq !== undefined) {\n args.push('FREQ', options.freq);\n }\n await this.executeCommand('RESTORE', ...args);\n return 'OK';\n }\n\n async time(): Promise<[string, string]> {\n this.assertConnected();\n const result = await this.executeCommand('TIME');\n const [seconds, microseconds] = result as [string, string];\n return [String(seconds), String(microseconds)];\n }\n\n async hget(key: string, field: string): Promise<string | null> {\n this.assertConnected();\n const result = await this.executeCommand('HGET', key, field);\n return result === null || result === undefined ? null : String(result);\n }\n\n async hset(key: string, field: string, value: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('HSET', key, field, value);\n return Number(result);\n }\n\n async hmset(key: string, data: Record<string, string>): Promise<'OK'> {\n this.assertConnected();\n const args: string[] = [key];\n for (const [field, value] of Object.entries(data)) {\n args.push(field, value);\n }\n await this.executeCommand('HMSET', ...args);\n return 'OK';\n }\n\n async hmget(key: string, ...fields: string[]): Promise<Array<string | null>> {\n this.assertConnected();\n const result = await this.executeCommand('HMGET', key, ...fields);\n return (result as unknown[]).map((v) => (v === null || v === undefined ? null : String(v)));\n }\n\n async hgetall(key: string): Promise<Record<string, string>> {\n this.assertConnected();\n const result = await this.executeCommand('HGETALL', key);\n return result as Record<string, string>;\n }\n\n async hdel(key: string, ...fields: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('HDEL', key, ...fields);\n return Number(result);\n }\n\n async hexists(key: string, field: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('HEXISTS', key, field);\n return Number(result);\n }\n\n async hkeys(key: string): Promise<string[]> {\n this.assertConnected();\n const result = await this.executeCommand('HKEYS', key);\n return result as string[];\n }\n\n async hvals(key: string): Promise<string[]> {\n this.assertConnected();\n const result = await this.executeCommand('HVALS', key);\n return result as string[];\n }\n\n async hlen(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('HLEN', key);\n return Number(result);\n }\n\n async hincrby(key: string, field: string, increment: number): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('HINCRBY', key, field, increment);\n return Number(result);\n }\n\n async hscan(key: string, cursor: number, options?: IScanOptions): Promise<[string, string[]]> {\n this.assertConnected();\n const args: unknown[] = [key, cursor];\n if (options?.match) {\n args.push('MATCH', options.match);\n }\n if (options?.count) {\n args.push('COUNT', options.count);\n }\n const result = await this.executeCommand('HSCAN', ...args);\n const [newCursor, fields] = result as [string, string[]];\n return [String(newCursor), fields];\n }\n\n async hsetnx(key: string, field: string, value: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('HSETNX', key, field, value);\n return Number(result);\n }\n\n async hincrbyfloat(key: string, field: string, increment: number): Promise<string> {\n this.assertConnected();\n const result = await this.executeCommand('HINCRBYFLOAT', key, field, increment);\n return String(result);\n }\n\n async hstrlen(key: string, field: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('HSTRLEN', key, field);\n return Number(result);\n }\n\n async hrandfield(key: string, count?: number, withValues?: boolean): Promise<string | string[] | null> {\n this.assertConnected();\n const args: unknown[] = [key];\n if (count !== undefined) {\n args.push(count);\n if (withValues) {\n args.push('WITHVALUES');\n }\n }\n const result = await this.executeCommand('HRANDFIELD', ...args);\n return result as string | string[] | null;\n }\n\n async lpush(key: string, ...values: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('LPUSH', key, ...values);\n return Number(result);\n }\n\n async rpush(key: string, ...values: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('RPUSH', key, ...values);\n return Number(result);\n }\n\n async lpop(key: string): Promise<string | null> {\n this.assertConnected();\n const result = await this.executeCommand('LPOP', key);\n return result === null || result === undefined ? null : String(result);\n }\n\n async rpop(key: string): Promise<string | null> {\n this.assertConnected();\n const result = await this.executeCommand('RPOP', key);\n return result === null || result === undefined ? null : String(result);\n }\n\n async llen(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('LLEN', key);\n return Number(result);\n }\n\n async lrange(key: string, start: number, stop: number): Promise<string[]> {\n this.assertConnected();\n const result = await this.executeCommand('LRANGE', key, start, stop);\n return result as string[];\n }\n\n async ltrim(key: string, start: number, stop: number): Promise<'OK'> {\n this.assertConnected();\n await this.executeCommand('LTRIM', key, start, stop);\n return 'OK';\n }\n\n async lindex(key: string, index: number): Promise<string | null> {\n this.assertConnected();\n const result = await this.executeCommand('LINDEX', key, index);\n return result === null || result === undefined ? null : String(result);\n }\n\n async lset(key: string, index: number, value: string): Promise<'OK'> {\n this.assertConnected();\n await this.executeCommand('LSET', key, index, value);\n return 'OK';\n }\n\n async linsert(key: string, position: 'BEFORE' | 'AFTER', pivot: string, element: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('LINSERT', key, position, pivot, element);\n return Number(result);\n }\n\n async lrem(key: string, count: number, element: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('LREM', key, count, element);\n return Number(result);\n }\n\n async lpos(key: string, element: string, options?: ILposOptions): Promise<number | number[] | null> {\n this.assertConnected();\n const args: unknown[] = [key, element];\n if (options?.rank !== undefined) {\n args.push('RANK', options.rank);\n }\n if (options?.count !== undefined) {\n args.push('COUNT', options.count);\n }\n if (options?.maxlen !== undefined) {\n args.push('MAXLEN', options.maxlen);\n }\n const result = await this.executeCommand('LPOS', ...args);\n if (result === null) return null;\n if (Array.isArray(result)) return result.map(Number);\n return Number(result);\n }\n\n async blpop(keys: string[], timeout: number): Promise<[string, string] | null> {\n this.assertConnected();\n const result = await this.executeCommand('BLPOP', ...keys, timeout);\n if (!result) return null;\n const [key, value] = result as [string, string];\n return [key, value];\n }\n\n async brpop(keys: string[], timeout: number): Promise<[string, string] | null> {\n this.assertConnected();\n const result = await this.executeCommand('BRPOP', ...keys, timeout);\n if (!result) return null;\n const [key, value] = result as [string, string];\n return [key, value];\n }\n\n async lmove(source: string, destination: string, from: 'LEFT' | 'RIGHT', to: 'LEFT' | 'RIGHT'): Promise<string | null> {\n this.assertConnected();\n const result = await this.executeCommand('LMOVE', source, destination, from, to);\n return result === null ? null : String(result);\n }\n\n async blmove(source: string, destination: string, from: 'LEFT' | 'RIGHT', to: 'LEFT' | 'RIGHT', timeout: number): Promise<string | null> {\n this.assertConnected();\n const result = await this.executeCommand('BLMOVE', source, destination, from, to, timeout);\n return result === null ? null : String(result);\n }\n\n async sadd(key: string, ...members: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SADD', key, ...members);\n return Number(result);\n }\n\n async srem(key: string, ...members: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SREM', key, ...members);\n return Number(result);\n }\n\n async smembers(key: string): Promise<string[]> {\n this.assertConnected();\n const result = await this.executeCommand('SMEMBERS', key);\n return result as string[];\n }\n\n async sismember(key: string, member: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SISMEMBER', key, member);\n return Number(result);\n }\n\n async scard(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SCARD', key);\n return Number(result);\n }\n\n async srandmember(key: string, count?: number): Promise<string | string[] | null> {\n this.assertConnected();\n const args: unknown[] = [key];\n if (count !== undefined) {\n args.push(count);\n }\n const result = await this.executeCommand('SRANDMEMBER', ...args);\n return result as string | string[] | null;\n }\n\n async spop(key: string, count?: number): Promise<string | string[] | null> {\n this.assertConnected();\n const args: unknown[] = [key];\n if (count !== undefined) {\n args.push(count);\n }\n const result = await this.executeCommand('SPOP', ...args);\n return result as string | string[] | null;\n }\n\n async sscan(key: string, cursor: number, options?: IScanOptions): Promise<[string, string[]]> {\n this.assertConnected();\n const args: unknown[] = [key, cursor];\n if (options?.match) {\n args.push('MATCH', options.match);\n }\n if (options?.count) {\n args.push('COUNT', options.count);\n }\n const result = await this.executeCommand('SSCAN', ...args);\n const [newCursor, members] = result as [string, string[]];\n return [String(newCursor), members];\n }\n\n async smove(source: string, destination: string, member: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SMOVE', source, destination, member);\n return Number(result);\n }\n\n async sinter(...keys: string[]): Promise<string[]> {\n this.assertConnected();\n const result = await this.executeCommand('SINTER', ...keys);\n return result as string[];\n }\n\n async sinterstore(destination: string, ...keys: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SINTERSTORE', destination, ...keys);\n return Number(result);\n }\n\n async sunion(...keys: string[]): Promise<string[]> {\n this.assertConnected();\n const result = await this.executeCommand('SUNION', ...keys);\n return result as string[];\n }\n\n async sunionstore(destination: string, ...keys: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SUNIONSTORE', destination, ...keys);\n return Number(result);\n }\n\n async sdiff(...keys: string[]): Promise<string[]> {\n this.assertConnected();\n const result = await this.executeCommand('SDIFF', ...keys);\n return result as string[];\n }\n\n async sdiffstore(destination: string, ...keys: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SDIFFSTORE', destination, ...keys);\n return Number(result);\n }\n\n async smismember(key: string, ...members: string[]): Promise<number[]> {\n this.assertConnected();\n const result = await this.executeCommand('SMISMEMBER', key, ...members);\n return (result as number[]).map(Number);\n }\n\n async zadd(key: string, ...args: Array<number | string>): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('ZADD', key, ...args);\n return Number(result);\n }\n\n async zrem(key: string, ...members: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('ZREM', key, ...members);\n return Number(result);\n }\n\n async zrange(key: string, start: number, stop: number, withScores?: boolean): Promise<string[]> {\n this.assertConnected();\n const args: unknown[] = [key, start, stop];\n if (withScores) {\n args.push('WITHSCORES');\n }\n const result = await this.executeCommand('ZRANGE', ...args);\n return result as string[];\n }\n\n async zrangebyscore(key: string, min: number | string, max: number | string, withScores?: boolean): Promise<string[]> {\n this.assertConnected();\n const args: unknown[] = [key, min, max];\n if (withScores) {\n args.push('WITHSCORES');\n }\n const result = await this.executeCommand('ZRANGEBYSCORE', ...args);\n return result as string[];\n }\n\n async zscore(key: string, member: string): Promise<string | null> {\n this.assertConnected();\n const result = await this.executeCommand('ZSCORE', key, member);\n return result === null || result === undefined ? null : String(result);\n }\n\n async zcard(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('ZCARD', key);\n return Number(result);\n }\n\n async zrank(key: string, member: string): Promise<number | null> {\n this.assertConnected();\n const result = await this.executeCommand('ZRANK', key, member);\n return result === null || result === undefined ? null : Number(result);\n }\n\n async zincrby(key: string, increment: number, member: string): Promise<string> {\n this.assertConnected();\n const result = await this.executeCommand('ZINCRBY', key, increment, member);\n return String(result);\n }\n\n async zscan(key: string, cursor: number, options?: IScanOptions): Promise<[string, string[]]> {\n this.assertConnected();\n const args: unknown[] = [key, cursor];\n if (options?.match) {\n args.push('MATCH', options.match);\n }\n if (options?.count) {\n args.push('COUNT', options.count);\n }\n const result = await this.executeCommand('ZSCAN', ...args);\n const [newCursor, members] = result as [string, string[]];\n return [String(newCursor), members];\n }\n\n async zrevrank(key: string, member: string): Promise<number | null> {\n this.assertConnected();\n const result = await this.executeCommand('ZREVRANK', key, member);\n return result === null || result === undefined ? null : Number(result);\n }\n\n async zcount(key: string, min: number | string, max: number | string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('ZCOUNT', key, min, max);\n return Number(result);\n }\n\n async zlexcount(key: string, min: string, max: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('ZLEXCOUNT', key, min, max);\n return Number(result);\n }\n\n async zpopmin(key: string, count?: number): Promise<string[]> {\n this.assertConnected();\n const args: unknown[] = [key];\n if (count !== undefined) {\n args.push(count);\n }\n const result = await this.executeCommand('ZPOPMIN', ...args);\n return result as string[];\n }\n\n async zpopmax(key: string, count?: number): Promise<string[]> {\n this.assertConnected();\n const args: unknown[] = [key];\n if (count !== undefined) {\n args.push(count);\n }\n const result = await this.executeCommand('ZPOPMAX', ...args);\n return result as string[];\n }\n\n async bzpopmin(keys: string[], timeout: number): Promise<[string, string, string] | null> {\n this.assertConnected();\n const result = await this.executeCommand('BZPOPMIN', ...keys, timeout);\n if (!result) return null;\n const [key, member, score] = result as [string, string, string];\n return [key, member, String(score)];\n }\n\n async bzpopmax(keys: string[], timeout: number): Promise<[string, string, string] | null> {\n this.assertConnected();\n const result = await this.executeCommand('BZPOPMAX', ...keys, timeout);\n if (!result) return null;\n const [key, member, score] = result as [string, string, string];\n return [key, member, String(score)];\n }\n\n async zunionstore(destination: string, keys: string[], options?: IZStoreOptions): Promise<number> {\n this.assertConnected();\n const args: unknown[] = [destination, keys.length, ...keys];\n if (options?.weights) {\n args.push('WEIGHTS', ...options.weights);\n }\n if (options?.aggregate) {\n args.push('AGGREGATE', options.aggregate);\n }\n const result = await this.executeCommand('ZUNIONSTORE', ...args);\n return Number(result);\n }\n\n async zinterstore(destination: string, keys: string[], options?: IZStoreOptions): Promise<number> {\n this.assertConnected();\n const args: unknown[] = [destination, keys.length, ...keys];\n if (options?.weights) {\n args.push('WEIGHTS', ...options.weights);\n }\n if (options?.aggregate) {\n args.push('AGGREGATE', options.aggregate);\n }\n const result = await this.executeCommand('ZINTERSTORE', ...args);\n return Number(result);\n }\n\n async zmscore(key: string, ...members: string[]): Promise<Array<string | null>> {\n this.assertConnected();\n const result = await this.executeCommand('ZMSCORE', key, ...members);\n return (result as unknown[]).map((v) => (v === null || v === undefined ? null : String(v)));\n }\n\n async zrandmember(key: string, count?: number, withScores?: boolean): Promise<string | string[] | null> {\n this.assertConnected();\n const args: unknown[] = [key];\n if (count !== undefined) {\n args.push(count);\n if (withScores) {\n args.push('WITHSCORES');\n }\n }\n const result = await this.executeCommand('ZRANDMEMBER', ...args);\n return result as string | string[] | null;\n }\n\n async zrevrangebyscore(key: string, max: number | string, min: number | string, options?: IZRangeByScoreOptions): Promise<string[]> {\n this.assertConnected();\n const args: unknown[] = [key, max, min];\n if (options?.withScores) {\n args.push('WITHSCORES');\n }\n if (options?.limit) {\n args.push('LIMIT', options.limit.offset, options.limit.count);\n }\n const result = await this.executeCommand('ZREVRANGEBYSCORE', ...args);\n return result as string[];\n }\n\n async publish(channel: string, message: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('PUBLISH', channel, message);\n return Number(result);\n }\n\n async subscribe(...channels: string[]): Promise<void> {\n this.assertConnected();\n await this.executeCommand('SUBSCRIBE', ...channels);\n }\n\n async unsubscribe(...channels: string[]): Promise<void> {\n this.assertConnected();\n await this.executeCommand('UNSUBSCRIBE', ...channels);\n }\n\n async psubscribe(...patterns: string[]): Promise<void> {\n this.assertConnected();\n await this.executeCommand('PSUBSCRIBE', ...patterns);\n }\n\n async punsubscribe(...patterns: string[]): Promise<void> {\n this.assertConnected();\n await this.executeCommand('PUNSUBSCRIBE', ...patterns);\n }\n\n async xadd(key: string, id: string, fields: Record<string, string>, options?: IStreamAddOptions): Promise<string> {\n this.assertConnected();\n const args: unknown[] = [key];\n\n // Add options before ID\n if (options?.noMkStream) {\n args.push('NOMKSTREAM');\n }\n if (options?.maxLen !== undefined) {\n args.push('MAXLEN');\n if (options.approximate) {\n args.push('~');\n }\n args.push(options.maxLen);\n }\n if (options?.minId !== undefined) {\n args.push('MINID');\n if (options.approximate) {\n args.push('~');\n }\n args.push(options.minId);\n }\n\n args.push(id);\n\n // Add fields\n for (const [field, value] of Object.entries(fields)) {\n args.push(field, value);\n }\n\n const result = await this.executeCommand('XADD', ...args);\n return String(result);\n }\n\n async xread(streams: Array<{ key: string; id: string }>, options?: IStreamReadOptions): Promise<StreamReadResult | null> {\n this.assertConnected();\n const args: unknown[] = [];\n\n if (options?.count !== undefined) {\n args.push('COUNT', options.count);\n }\n if (options?.block !== undefined) {\n args.push('BLOCK', options.block);\n }\n\n args.push('STREAMS');\n for (const stream of streams) {\n args.push(stream.key);\n }\n for (const stream of streams) {\n args.push(stream.id);\n }\n\n const result = await this.executeCommand('XREAD', ...args);\n if (!result) return null;\n\n return this.parseStreamReadResult(result as unknown[]);\n }\n\n async xreadgroup(group: string, consumer: string, streams: Array<{ key: string; id: string }>, options?: IStreamReadGroupOptions): Promise<StreamReadResult | null> {\n this.assertConnected();\n const args: unknown[] = ['GROUP', group, consumer];\n\n if (options?.count !== undefined) {\n args.push('COUNT', options.count);\n }\n if (options?.block !== undefined) {\n args.push('BLOCK', options.block);\n }\n if (options?.noAck) {\n args.push('NOACK');\n }\n\n args.push('STREAMS');\n for (const stream of streams) {\n args.push(stream.key);\n }\n for (const stream of streams) {\n args.push(stream.id);\n }\n\n const result = await this.executeCommand('XREADGROUP', ...args);\n if (!result) return null;\n\n return this.parseStreamReadResult(result as unknown[]);\n }\n\n async xrange(key: string, start: string, end: string, options?: { count?: number }): Promise<IStreamEntry[]> {\n this.assertConnected();\n const args: unknown[] = [key, start, end];\n\n if (options?.count !== undefined) {\n args.push('COUNT', options.count);\n }\n\n const result = await this.executeCommand('XRANGE', ...args);\n return this.parseStreamEntries(result as unknown[]);\n }\n\n async xrevrange(key: string, end: string, start: string, options?: { count?: number }): Promise<IStreamEntry[]> {\n this.assertConnected();\n const args: unknown[] = [key, end, start];\n\n if (options?.count !== undefined) {\n args.push('COUNT', options.count);\n }\n\n const result = await this.executeCommand('XREVRANGE', ...args);\n return this.parseStreamEntries(result as unknown[]);\n }\n\n async xlen(key: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('XLEN', key);\n return Number(result);\n }\n\n async xinfo(key: string): Promise<IStreamInfo> {\n this.assertConnected();\n const result = await this.executeCommand('XINFO', 'STREAM', key);\n return this.parseIStreamInfo(result as unknown[]);\n }\n\n async xtrim(key: string, maxLen: number, approximate?: boolean): Promise<number> {\n this.assertConnected();\n const args: unknown[] = [key, 'MAXLEN'];\n if (approximate) {\n args.push('~');\n }\n args.push(maxLen);\n\n const result = await this.executeCommand('XTRIM', ...args);\n return Number(result);\n }\n\n async xgroupCreate(key: string, group: string, id: string, mkstream?: boolean): Promise<'OK'> {\n this.assertConnected();\n const args: unknown[] = [key, group, id];\n if (mkstream) {\n args.push('MKSTREAM');\n }\n\n await this.executeCommand('XGROUP', 'CREATE', ...args);\n return 'OK';\n }\n\n async xgroupDestroy(key: string, group: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('XGROUP', 'DESTROY', key, group);\n return Number(result);\n }\n\n async xgroupDelConsumer(key: string, group: string, consumer: string): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('XGROUP', 'DELCONSUMER', key, group, consumer);\n return Number(result);\n }\n\n async xgroupSetId(key: string, group: string, id: string): Promise<'OK'> {\n this.assertConnected();\n await this.executeCommand('XGROUP', 'SETID', key, group, id);\n return 'OK';\n }\n\n async xack(key: string, group: string, ...ids: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('XACK', key, group, ...ids);\n return Number(result);\n }\n\n async xpending(key: string, group: string): Promise<IStreamPendingInfo> {\n this.assertConnected();\n const result = await this.executeCommand('XPENDING', key, group);\n return this.parseIStreamPendingInfo(result as unknown[]);\n }\n\n async xpendingRange(key: string, group: string, start: string, end: string, count: number, consumer?: string): Promise<IStreamPendingEntry[]> {\n this.assertConnected();\n const args: unknown[] = [key, group, start, end, count];\n if (consumer) {\n args.push(consumer);\n }\n\n const result = await this.executeCommand('XPENDING', ...args);\n return this.parseStreamPendingEntries(result as unknown[]);\n }\n\n async xclaim(key: string, group: string, consumer: string, minIdleTime: number, ...ids: string[]): Promise<IStreamEntry[]> {\n this.assertConnected();\n const result = await this.executeCommand('XCLAIM', key, group, consumer, minIdleTime, ...ids);\n return this.parseStreamEntries(result as unknown[]);\n }\n\n async xdel(key: string, ...ids: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('XDEL', key, ...ids);\n return Number(result);\n }\n\n private parseStreamReadResult(result: unknown[]): StreamReadResult {\n return result.map((streamData) => {\n const [key, entries] = streamData as [string, unknown[]];\n return {\n key,\n entries: this.parseStreamEntries(entries),\n };\n });\n }\n\n private parseStreamEntries(entries: unknown[]): IStreamEntry[] {\n return entries.map((entry) => {\n const [id, fields] = entry as [string, string[]];\n return {\n id,\n fields: this.parseFieldArray(fields),\n };\n });\n }\n\n private parseFieldArray(fields: string[]): Record<string, string> {\n const result: Record<string, string> = {};\n for (let i = 0; i < fields.length; i += 2) {\n const key = fields[i];\n const value = fields[i + 1];\n if (key !== undefined && value !== undefined) {\n result[key] = value;\n }\n }\n return result;\n }\n\n private parseIStreamInfo(result: unknown[]): IStreamInfo {\n const info: Record<string, unknown> = {};\n for (let i = 0; i < result.length; i += 2) {\n const key = String(result[i]).toLowerCase().replace(/-/g, '');\n info[key] = result[i + 1];\n }\n\n return {\n length: Number(info['length'] ?? 0),\n groups: Number(info['groups'] ?? 0),\n firstEntry: info['firstentry'] ? (this.parseStreamEntries([info['firstentry']])[0] ?? null) : null,\n lastEntry: info['lastentry'] ? (this.parseStreamEntries([info['lastentry']])[0] ?? null) : null,\n lastGeneratedId: String(info['lastgeneratedid'] ?? '0-0'),\n radixTreeKeys: Number(info['radixtreekeys'] ?? 0),\n radixTreeNodes: Number(info['radixtreenodes'] ?? 0),\n };\n }\n\n private parseIStreamPendingInfo(result: unknown[]): IStreamPendingInfo {\n const [count, minId, maxId, consumers] = result as [number, string | null, string | null, unknown[] | null];\n return {\n count: Number(count),\n minId,\n maxId,\n consumers: consumers\n ? consumers.map((c) => {\n const [name, pending] = c as [string, string];\n return { name, count: Number(pending) };\n })\n : [],\n };\n }\n\n private parseStreamPendingEntries(result: unknown[]): IStreamPendingEntry[] {\n return result.map((entry) => {\n const [id, consumer, idleTime, deliveryCount] = entry as [string, string, number, number];\n return {\n id,\n consumer,\n idleTime: Number(idleTime),\n deliveryCount: Number(deliveryCount),\n };\n });\n }\n\n pipeline(): IPipeline {\n this.assertConnected();\n return this.createPipeline();\n }\n\n multi(): IMulti {\n this.assertConnected();\n return this.createMulti();\n }\n\n /** Registry mapping SHA → script source for NOSCRIPT fallback */\n private readonly scriptRegistry = new Map<string, string>();\n\n async eval(script: string, keys: string[], args: Array<string | number>): Promise<unknown> {\n this.assertConnected();\n const result = await this.executeCommand('EVAL', script, keys.length, ...keys, ...args);\n return result;\n }\n\n async evalsha(sha: string, keys: string[], args: Array<string | number>): Promise<unknown> {\n this.assertConnected();\n try {\n return await this.executeCommand('EVALSHA', sha, keys.length, ...keys, ...args);\n } catch (error) {\n if (this.isNoscriptError(error)) {\n const script = this.scriptRegistry.get(sha);\n if (script) {\n return this.eval(script, keys, args);\n }\n }\n throw error;\n }\n }\n\n async scriptLoad(script: string): Promise<string> {\n this.assertConnected();\n const result = await this.executeCommand('SCRIPT', 'LOAD', script);\n const sha = String(result);\n this.scriptRegistry.set(sha, script);\n return sha;\n }\n\n private isNoscriptError(error: unknown): boolean {\n const msg = (error as Error)?.message ?? '';\n return msg.includes('NOSCRIPT');\n }\n\n async scriptExists(...shas: string[]): Promise<number[]> {\n this.assertConnected();\n const result = await this.executeCommand('SCRIPT', 'EXISTS', ...shas);\n return (result as number[]).map(Number);\n }\n\n async scriptFlush(): Promise<'OK'> {\n this.assertConnected();\n await this.executeCommand('SCRIPT', 'FLUSH');\n return 'OK';\n }\n\n async pfadd(key: string, ...elements: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('PFADD', key, ...elements);\n return Number(result);\n }\n\n async pfcount(...keys: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('PFCOUNT', ...keys);\n return Number(result);\n }\n\n async pfmerge(destination: string, ...sources: string[]): Promise<'OK'> {\n this.assertConnected();\n await this.executeCommand('PFMERGE', destination, ...sources);\n return 'OK';\n }\n\n async geoadd(key: string, ...members: Array<number | string>): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('GEOADD', key, ...members);\n return Number(result);\n }\n\n async geodist(key: string, member1: string, member2: string, unit?: GeoUnit): Promise<string | null> {\n this.assertConnected();\n const args: unknown[] = [key, member1, member2];\n if (unit) {\n args.push(unit);\n }\n const result = await this.executeCommand('GEODIST', ...args);\n return result === null ? null : String(result);\n }\n\n async geohash(key: string, ...members: string[]): Promise<Array<string | null>> {\n this.assertConnected();\n const result = await this.executeCommand('GEOHASH', key, ...members);\n return (result as unknown[]).map((v) => (v === null ? null : String(v)));\n }\n\n async geopos(key: string, ...members: string[]): Promise<Array<[string, string] | null>> {\n this.assertConnected();\n const result = await this.executeCommand('GEOPOS', key, ...members);\n return (result as Array<[string, string] | null>).map((pos) => {\n if (!pos) return null;\n return [String(pos[0]), String(pos[1])];\n });\n }\n\n async geosearch(key: string, options: IGeoSearchOptions): Promise<Array<string | IGeoSearchResult>> {\n this.assertConnected();\n const args: unknown[] = [key];\n\n // From member or coordinates\n if (options.member) {\n args.push('FROMMEMBER', options.member);\n } else if (options.coord) {\n args.push('FROMLONLAT', options.coord.longitude, options.coord.latitude);\n }\n\n // By radius or box\n if (options.radius) {\n args.push('BYRADIUS', options.radius.value, options.radius.unit);\n } else if (options.box) {\n args.push('BYBOX', options.box.width, options.box.height, options.box.unit);\n }\n\n // Optional parameters\n if (options.sort) {\n args.push(options.sort);\n }\n if (options.count !== undefined) {\n args.push('COUNT', options.count);\n if (options.any) {\n args.push('ANY');\n }\n }\n if (options.withCoord) {\n args.push('WITHCOORD');\n }\n if (options.withDist) {\n args.push('WITHDIST');\n }\n if (options.withHash) {\n args.push('WITHHASH');\n }\n\n const result = await this.executeCommand('GEOSEARCH', ...args);\n return this.parseIGeoSearchResult(result as unknown[], options);\n }\n\n async geosearchstore(destination: string, source: string, options: IGeoSearchOptions & { storedist?: boolean }): Promise<number> {\n this.assertConnected();\n const args: unknown[] = [destination, source];\n\n // From member or coordinates\n if (options.member) {\n args.push('FROMMEMBER', options.member);\n } else if (options.coord) {\n args.push('FROMLONLAT', options.coord.longitude, options.coord.latitude);\n }\n\n // By radius or box\n if (options.radius) {\n args.push('BYRADIUS', options.radius.value, options.radius.unit);\n } else if (options.box) {\n args.push('BYBOX', options.box.width, options.box.height, options.box.unit);\n }\n\n // Optional parameters\n if (options.sort) {\n args.push(options.sort);\n }\n if (options.count !== undefined) {\n args.push('COUNT', options.count);\n if (options.any) {\n args.push('ANY');\n }\n }\n if (options.storedist) {\n args.push('STOREDIST');\n }\n\n const result = await this.executeCommand('GEOSEARCHSTORE', ...args);\n return Number(result);\n }\n\n private parseIGeoSearchResult(result: unknown[], options: IGeoSearchOptions): Array<string | IGeoSearchResult> {\n const hasExtra = options.withCoord || options.withDist || options.withHash;\n if (!hasExtra) {\n return result as string[];\n }\n\n return result.map((item) => {\n if (!Array.isArray(item)) {\n return item as string;\n }\n\n const geoResult: IGeoSearchResult = { member: String(item[0]) };\n let idx = 1;\n\n if (options.withDist && item[idx] !== undefined) {\n geoResult.distance = String(item[idx]);\n idx++;\n }\n if (options.withHash && item[idx] !== undefined) {\n geoResult.hash = Number(item[idx]);\n idx++;\n }\n if (options.withCoord && item[idx] !== undefined) {\n const coords = item[idx] as [string, string];\n geoResult.coordinates = [String(coords[0]), String(coords[1])];\n }\n\n return geoResult;\n });\n }\n\n async setbit(key: string, offset: number, value: 0 | 1): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('SETBIT', key, offset, value);\n return Number(result);\n }\n\n async getbit(key: string, offset: number): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('GETBIT', key, offset);\n return Number(result);\n }\n\n async bitcount(key: string, start?: number, end?: number, mode?: 'BYTE' | 'BIT'): Promise<number> {\n this.assertConnected();\n const args: unknown[] = [key];\n if (start !== undefined && end !== undefined) {\n args.push(start, end);\n if (mode) {\n args.push(mode);\n }\n }\n const result = await this.executeCommand('BITCOUNT', ...args);\n return Number(result);\n }\n\n async bitop(operation: 'AND' | 'OR' | 'XOR' | 'NOT', destKey: string, ...keys: string[]): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('BITOP', operation, destKey, ...keys);\n return Number(result);\n }\n\n async bitpos(key: string, bit: 0 | 1, start?: number, end?: number, mode?: 'BYTE' | 'BIT'): Promise<number> {\n this.assertConnected();\n const args: unknown[] = [key, bit];\n if (start !== undefined) {\n args.push(start);\n if (end !== undefined) {\n args.push(end);\n if (mode) {\n args.push(mode);\n }\n }\n }\n const result = await this.executeCommand('BITPOS', ...args);\n return Number(result);\n }\n\n async flushdb(): Promise<'OK'> {\n this.assertConnected();\n await this.executeCommand('FLUSHDB');\n return 'OK';\n }\n\n async flushall(): Promise<'OK'> {\n this.assertConnected();\n await this.executeCommand('FLUSHALL');\n return 'OK';\n }\n\n async info(section?: string): Promise<string> {\n this.assertConnected();\n const args: string[] = section ? [section] : [];\n const result = await this.executeCommand('INFO', ...args);\n return String(result);\n }\n\n async dbsize(): Promise<number> {\n this.assertConnected();\n const result = await this.executeCommand('DBSIZE');\n return Number(result);\n }\n\n async cluster(subcommand: string, ...args: Array<string | number>): Promise<unknown> {\n this.assertConnected();\n return this.executeCommand('CLUSTER', subcommand, ...args);\n }\n\n async sentinel(subcommand: string, ...args: Array<string | number>): Promise<unknown> {\n this.assertConnected();\n return this.executeCommand('SENTINEL', subcommand, ...args);\n }\n\n on(event: DriverEvent, handler: DriverEventHandler): void {\n this.eventEmitter.on(event, handler);\n }\n\n once(event: DriverEvent, handler: DriverEventHandler): void {\n this.eventEmitter.once(event, handler);\n }\n\n off(event: DriverEvent, handler: DriverEventHandler): void {\n this.eventEmitter.off(event, handler);\n }\n\n removeAllListeners(event?: DriverEvent): void {\n if (event) {\n this.eventEmitter.removeAllListeners(event);\n } else {\n this.eventEmitter.removeAllListeners();\n }\n }\n\n /**\n * Asserts that driver is connected.\n * @throws DriverError if not connected\n */\n protected assertConnected(): void {\n if (!this.connected) {\n throw new DriverError('Driver is not connected. Call connect() first.', 'DRIVER_NOT_CONNECTED' as never);\n }\n }\n\n /**\n * Emits driver event.\n */\n protected emit(event: DriverEvent, data?: unknown): void {\n this.eventEmitter.emit(event, data);\n }\n\n /**\n * Logs message if logging is enabled.\n */\n protected log(message: string, data?: unknown): void {\n if (this.enableLogging) {\n if (data) {\n this.logger.debug(`${message} ${JSON.stringify(data)}`);\n } else {\n this.logger.debug(message);\n }\n }\n }\n\n /**\n * Wraps execution with timeout.\n */\n protected async withTimeout<T>(promise: Promise<T>, timeoutMs: number, operation: string): Promise<T> {\n return Promise.race([promise, new Promise<T>((_, reject) => setTimeout(() => reject(new TimeoutError(operation, timeoutMs)), timeoutMs))]);\n }\n}\n","/**\n * Redis driver abstraction interface.\n *\n * Complete interface for all Redis operations.\n * All Redis operations MUST go through this interface.\n * NEVER import ioredis or redis directly.\n *\n * @example\n * ```typescript\n * @Injectable()\n * export class CacheService {\n * constructor(\n * @Inject(REDIS_DRIVER) private readonly driver: IRedisDriver\n * ) {}\n * }\n * ```\n */\nexport interface IRedisDriver {\n /**\n * Connects to Redis.\n * Called automatically by ClientManager.\n */\n connect(): Promise<void>;\n\n /**\n * Disconnects from Redis gracefully.\n * Called automatically on module destroy.\n */\n disconnect(): Promise<void>;\n\n /**\n * Checks if connected to Redis.\n */\n isConnected(): boolean;\n\n /**\n * Pings Redis server.\n * @returns 'PONG' or custom message\n */\n ping(message?: string): Promise<string>;\n\n /**\n * Selects database.\n * @param db - Database number (0-15)\n */\n select(db: number): Promise<void>;\n\n /**\n * Gets value of a key.\n * @returns Value or null if key doesn't exist\n */\n get(key: string): Promise<string | null>;\n\n /**\n * Sets value of a key with optional TTL.\n * @returns 'OK' if successful, null otherwise\n */\n set(key: string, value: string, options?: ISetOptions): Promise<'OK' | null>;\n\n /**\n * Gets multiple values.\n * @returns Array of values (null for non-existent keys)\n */\n mget(...keys: string[]): Promise<Array<string | null>>;\n\n /**\n * Sets multiple key-value pairs.\n * @returns 'OK' if successful\n */\n mset(data: Record<string, string>): Promise<'OK'>;\n\n /**\n * Sets value only if key doesn't exist.\n * @returns 1 if set, 0 if not set\n */\n setnx(key: string, value: string): Promise<number>;\n\n /**\n * Sets value with expiration in seconds.\n * @returns 'OK' if successful\n */\n setex(key: string, seconds: number, value: string): Promise<'OK'>;\n\n /**\n * Gets value and deletes key atomically.\n * @returns Value or null if key doesn't exist\n */\n getdel(key: string): Promise<string | null>;\n\n /**\n * Gets value and sets expiration atomically.\n * @returns Value or null if key doesn't exist\n */\n getex(key: string, options: { ex?: number; px?: number }): Promise<string | null>;\n\n /**\n * Increments integer value by 1.\n * @returns New value after increment\n */\n incr(key: string): Promise<number>;\n\n /**\n * Increments integer value by amount.\n * @returns New value after increment\n */\n incrby(key: string, increment: number): Promise<number>;\n\n /**\n * Decrements integer value by 1.\n * @returns New value after decrement\n */\n decr(key: string): Promise<number>;\n\n /**\n * Decrements integer value by amount.\n * @returns New value after decrement\n */\n decrby(key: string, decrement: number): Promise<number>;\n\n /**\n * Appends value to key.\n * @returns Length of string after append\n */\n append(key: string, value: string): Promise<number>;\n\n /**\n * Gets length of string value.\n * @returns Length of string, 0 if key doesn't exist\n */\n strlen(key: string): Promise<number>;\n\n /**\n * Increments value by float amount.\n * @returns New value after increment\n */\n incrbyfloat(key: string, increment: number): Promise<string>;\n\n /**\n * Gets substring of string value.\n * @param start - Start offset (can be negative)\n * @param end - End offset (can be negative)\n * @returns Substring\n */\n getrange(key: string, start: number, end: number): Promise<string>;\n\n /**\n * Overwrites part of string at offset.\n * @returns Length of string after modification\n */\n setrange(key: string, offset: number, value: string): Promise<number>;\n\n /**\n * Sets multiple keys only if none exist.\n * @returns 1 if all keys set, 0 if no keys set\n */\n msetnx(data: Record<string, string>): Promise<number>;\n\n /**\n * Deletes one or more keys.\n * @returns Number of keys deleted\n */\n del(...keys: string[]): Promise<number>;\n\n /**\n * Checks if key exists.\n * @returns Number of existing keys\n */\n exists(...keys: string[]): Promise<number>;\n\n /**\n * Sets TTL on a key (seconds).\n * @returns 1 if timeout was set, 0 if key doesn't exist\n */\n expire(key: string, seconds: number): Promise<number>;\n\n /**\n * Sets TTL on a key (milliseconds).\n * @returns 1 if timeout was set, 0 if key doesn't exist\n */\n pexpire(key: string, milliseconds: number): Promise<number>;\n\n /**\n * Sets absolute expiration time (Unix timestamp in seconds).\n * @returns 1 if timeout was set, 0 if key doesn't exist\n */\n expireat(key: string, timestamp: number): Promise<number>;\n\n /**\n * Gets TTL of a key (seconds).\n * @returns TTL in seconds, -1 if no TTL, -2 if key doesn't exist\n */\n ttl(key: string): Promise<number>;\n\n /**\n * Gets TTL of a key (milliseconds).\n * @returns TTL in milliseconds, -1 if no TTL, -2 if key doesn't exist\n */\n pttl(key: string): Promise<number>;\n\n /**\n * Removes expiration from key.\n * @returns 1 if expiration removed, 0 if key has no expiration\n */\n persist(key: string): Promise<number>;\n\n /**\n * Renames key.\n * @returns 'OK' if successful\n * @throws Error if source key doesn't exist\n */\n rename(key: string, newKey: string): Promise<'OK'>;\n\n /**\n * Renames key only if new key doesn't exist.\n * @returns 1 if renamed, 0 if new key exists\n */\n renamenx(key: string, newKey: string): Promise<number>;\n\n /**\n * Gets type of value stored at key.\n * @returns Type name: 'string', 'list', 'set', 'zset', 'hash', 'stream', 'none'\n */\n type(key: string): Promise<string>;\n\n /**\n * Scans keys matching pattern.\n * @returns Cursor and array of keys\n */\n scan(cursor: number, options?: IScanOptions): Promise<[string, string[]]>;\n\n /**\n * Deletes keys asynchronously (non-blocking).\n * Better for large keys than del.\n * @returns Number of keys unlinked\n */\n unlink(...keys: string[]): Promise<number>;\n\n /**\n * Copies key to another key.\n * @param destination - Destination key\n * @param options - Copy options (replace, db)\n * @returns 1 if copied, 0 if not\n */\n copy(source: string, destination: string, options?: ICopyOptions): Promise<number>;\n\n /**\n * Finds all keys matching pattern.\n * WARNING: Use SCAN in production for large keyspaces.\n * @returns Array of matching keys\n */\n keys(pattern: string): Promise<string[]>;\n\n /**\n * Alters the last access time of keys.\n * @returns Number of keys touched\n */\n touch(...keys: string[]): Promise<number>;\n\n /**\n * Returns the internal encoding of a Redis object.\n * @param subcommand - ENCODING, FREQ, IDLETIME, REFCOUNT\n * @returns Object info\n */\n object(subcommand: 'ENCODING' | 'FREQ' | 'IDLETIME' | 'REFCOUNT', key: string): Promise<string | number | null>;\n\n /**\n * Serializes the value at key.\n * @returns Serialized value or null\n */\n dump(key: string): Promise<string | null>;\n\n /**\n * Deserializes and stores value at key.\n * @param ttl - TTL in milliseconds (0 = no expiry)\n * @param serializedValue - Serialized value from dump\n * @param options - Restore options\n * @returns 'OK' if successful\n */\n restore(key: string, ttl: number, serializedValue: string, options?: IRestoreOptions): Promise<'OK'>;\n\n /**\n * Returns server time.\n * @returns [unix timestamp in seconds, microseconds]\n */\n time(): Promise<[string, string]>;\n\n /**\n * Gets value of hash field.\n * @returns Value or null if field doesn't exist\n */\n hget(key: string, field: string): Promise<string | null>;\n\n /**\n * Sets value of hash field.\n * @returns 1 if new field, 0 if field updated\n */\n hset(key: string, field: string, value: string): Promise<number>;\n\n /**\n * Sets multiple hash fields.\n * @returns 'OK' if successful\n */\n hmset(key: string, data: Record<string, string>): Promise<'OK'>;\n\n /**\n * Gets multiple hash field values.\n * @returns Array of values (null for non-existent fields)\n */\n hmget(key: string, ...fields: string[]): Promise<Array<string | null>>;\n\n /**\n * Gets all fields and values of hash.\n * @returns Object with field-value pairs\n */\n hgetall(key: string): Promise<Record<string, string>>;\n\n /**\n * Deletes hash fields.\n * @returns Number of fields deleted\n */\n hdel(key: string, ...fields: string[]): Promise<number>;\n\n /**\n * Checks if hash field exists.\n * @returns 1 if field exists, 0 otherwise\n */\n hexists(key: string, field: string): Promise<number>;\n\n /**\n * Gets all field names in hash.\n */\n hkeys(key: string): Promise<string[]>;\n\n /**\n * Gets all values in hash.\n */\n hvals(key: string): Promise<string[]>;\n\n /**\n * Gets number of fields in hash.\n */\n hlen(key: string): Promise<number>;\n\n /**\n * Increments hash field by integer.\n * @returns New value after increment\n */\n hincrby(key: string, field: string, increment: number): Promise<number>;\n\n /**\n * Scans hash fields.\n * @returns Cursor and array of field-value pairs\n */\n hscan(key: string, cursor: number, options?: IScanOptions): Promise<[string, string[]]>;\n\n /**\n * Sets hash field only if it doesn't exist.\n * @returns 1 if field set, 0 if field exists\n */\n hsetnx(key: string, field: string, value: string): Promise<number>;\n\n /**\n * Increments hash field by float.\n * @returns New value as string\n */\n hincrbyfloat(key: string, field: string, increment: number): Promise<string>;\n\n /**\n * Gets string length of hash field value.\n * @returns Length of field value, 0 if field/key doesn't exist\n */\n hstrlen(key: string, field: string): Promise<number>;\n\n /**\n * Gets random fields from hash.\n * @param count - Number of fields (negative for duplicates allowed)\n * @param withValues - Include values\n * @returns Random field(s) or field-value pairs\n */\n hrandfield(key: string, count?: number, withValues?: boolean): Promise<string | string[] | null>;\n\n /**\n * Pushes values to left of list.\n * @returns Length of list after push\n */\n lpush(key: string, ...values: string[]): Promise<number>;\n\n /**\n * Pushes values to right of list.\n * @returns Length of list after push\n */\n rpush(key: string, ...values: string[]): Promise<number>;\n\n /**\n * Pops value from left of list.\n * @returns Value or null if list is empty\n */\n lpop(key: string): Promise<string | null>;\n\n /**\n * Pops value from right of list.\n * @returns Value or null if list is empty\n */\n rpop(key: string): Promise<string | null>;\n\n /**\n * Gets list length.\n * @returns Number of elements\n */\n llen(key: string): Promise<number>;\n\n /**\n * Gets list range.\n * @returns Array of values\n */\n lrange(key: string, start: number, stop: number): Promise<string[]>;\n\n /**\n * Trims list to specified range.\n * @returns 'OK' if successful\n */\n ltrim(key: string, start: number, stop: number): Promise<'OK'>;\n\n /**\n * Gets element at index.\n * @returns Value or null if index out of range\n */\n lindex(key: string, index: number): Promise<string | null>;\n\n /**\n * Sets element at index.\n * @returns 'OK' if successful\n */\n lset(key: string, index: number, value: string): Promise<'OK'>;\n\n /**\n * Inserts element before or after pivot.\n * @param position - BEFORE or AFTER\n * @param pivot - Reference element\n * @param element - Element to insert\n * @returns List length, -1 if pivot not found\n */\n linsert(key: string, position: 'BEFORE' | 'AFTER', pivot: string, element: string): Promise<number>;\n\n /**\n * Removes elements from list.\n * @param count - Number to remove (0=all, positive=head, negative=tail)\n * @param element - Element to remove\n * @returns Number of removed elements\n */\n lrem(key: string, count: number, element: string): Promise<number>;\n\n /**\n * Gets index of element in list.\n * @param options - RANK, COUNT, MAXLEN options\n * @returns Index or null if not found\n */\n lpos(key: string, element: string, options?: ILposOptions): Promise<number | number[] | null>;\n\n /**\n * Blocking left pop from list.\n * @param timeout - Timeout in seconds (0 = block forever)\n * @returns [key, element] or null on timeout\n */\n blpop(keys: string[], timeout: number): Promise<[string, string] | null>;\n\n /**\n * Blocking right pop from list.\n * @param timeout - Timeout in seconds (0 = block forever)\n * @returns [key, element] or null on timeout\n */\n brpop(keys: string[], timeout: number): Promise<[string, string] | null>;\n\n /**\n * Atomically moves element between lists.\n * @param source - Source list key\n * @param destination - Destination list key\n * @param from - LEFT or RIGHT\n * @param to - LEFT or RIGHT\n * @returns Moved element or null\n */\n lmove(source: string, destination: string, from: 'LEFT' | 'RIGHT', to: 'LEFT' | 'RIGHT'): Promise<string | null>;\n\n /**\n * Blocking version of lmove.\n * @param timeout - Timeout in seconds\n * @returns Moved element or null on timeout\n */\n blmove(source: string, destination: string, from: 'LEFT' | 'RIGHT', to: 'LEFT' | 'RIGHT', timeout: number): Promise<string | null>;\n\n /**\n * Adds members to set.\n * @returns Number of members added\n */\n sadd(key: string, ...members: string[]): Promise<number>;\n\n /**\n * Removes members from set.\n * @returns Number of members removed\n */\n srem(key: string, ...members: string[]): Promise<number>;\n\n /**\n * Gets all members of set.\n */\n smembers(key: string): Promise<string[]>;\n\n /**\n * Checks if member is in set.\n * @returns 1 if member exists, 0 otherwise\n */\n sismember(key: string, member: string): Promise<number>;\n\n /**\n * Gets number of members in set.\n */\n scard(key: string): Promise<number>;\n\n /**\n * Gets random member from set.\n * @returns Random member or null if set is empty\n */\n srandmember(key: string, count?: number): Promise<string | string[] | null>;\n\n /**\n * Pops random member from set.\n * @returns Random member or null if set is empty\n */\n spop(key: string, count?: number): Promise<string | string[] | null>;\n\n /**\n * Scans set members.\n * @returns Cursor and array of members\n */\n sscan(key: string, cursor: number, options?: IScanOptions): Promise<[string, string[]]>;\n\n /**\n * Moves member from one set to another.\n * @returns 1 if moved, 0 if member not in source\n */\n smove(source: string, destination: string, member: string): Promise<number>;\n\n /**\n * Returns intersection of sets.\n * @returns Array of common members\n */\n sinter(...keys: string[]): Promise<string[]>;\n\n /**\n * Stores intersection of sets.\n * @returns Number of members in result\n */\n sinterstore(destination: string, ...keys: string[]): Promise<number>;\n\n /**\n * Returns union of sets.\n * @returns Array of all unique members\n */\n sunion(...keys: string[]): Promise<string[]>;\n\n /**\n * Stores union of sets.\n * @returns Number of members in result\n */\n sunionstore(destination: string, ...keys: string[]): Promise<number>;\n\n /**\n * Returns difference of sets (first - others).\n * @returns Array of members in first set but not in others\n */\n sdiff(...keys: string[]): Promise<string[]>;\n\n /**\n * Stores difference of sets.\n * @returns Number of members in result\n */\n sdiffstore(destination: string, ...keys: string[]): Promise<number>;\n\n /**\n * Checks if multiple members are in set.\n * @returns Array of 1/0 for each member\n */\n smismember(key: string, ...members: string[]): Promise<number[]>;\n\n /**\n * Adds members to sorted set with scores.\n * @returns Number of members added\n */\n zadd(key: string, ...args: Array<number | string>): Promise<number>;\n\n /**\n * Removes members from sorted set.\n * @returns Number of members removed\n */\n zrem(key: string, ...members: string[]): Promise<number>;\n\n /**\n * Gets members in range by index.\n */\n zrange(key: string, start: number, stop: number, withScores?: boolean): Promise<string[]>;\n\n /**\n * Gets members in range by score.\n */\n zrangebyscore(key: string, min: number | string, max: number | string, withScores?: boolean): Promise<string[]>;\n\n /**\n * Gets score of member.\n * @returns Score or null if member doesn't exist\n */\n zscore(key: string, member: string): Promise<string | null>;\n\n /**\n * Gets number of members in sorted set.\n */\n zcard(key: string): Promise<number>;\n\n /**\n * Gets rank of member (0-based, lowest score first).\n * @returns Rank or null if member doesn't exist\n */\n zrank(key: string, member: string): Promise<number | null>;\n\n /**\n * Increments score of member.\n * @returns New score\n */\n zincrby(key: string, increment: number, member: string): Promise<string>;\n\n /**\n * Scans sorted set members.\n * @returns Cursor and array of member-score pairs\n */\n zscan(key: string, cursor: number, options?: IScanOptions): Promise<[string, string[]]>;\n\n /**\n * Gets reverse rank of member (highest score first).\n * @returns Rank or null if member doesn't exist\n */\n zrevrank(key: string, member: string): Promise<number | null>;\n\n /**\n * Counts members with scores in range.\n * @returns Number of members\n */\n zcount(key: string, min: number | string, max: number | string): Promise<number>;\n\n /**\n * Counts members in lexicographical range.\n * @returns Number of members\n */\n zlexcount(key: string, min: string, max: string): Promise<number>;\n\n /**\n * Removes and returns members with lowest scores.\n * @returns Array of [member, score] pairs\n */\n zpopmin(key: string, count?: number): Promise<string[]>;\n\n /**\n * Removes and returns members with highest scores.\n * @returns Array of [member, score] pairs\n */\n zpopmax(key: string, count?: number): Promise<string[]>;\n\n /**\n * Blocking pop from sorted set (lowest score).\n * @param timeout - Timeout in seconds\n * @returns [key, member, score] or null on timeout\n */\n bzpopmin(keys: string[], timeout: number): Promise<[string, string, string] | null>;\n\n /**\n * Blocking pop from sorted set (highest score).\n * @param timeout - Timeout in seconds\n * @returns [key, member, score] or null on timeout\n */\n bzpopmax(keys: string[], timeout: number): Promise<[string, string, string] | null>;\n\n /**\n * Computes union of sorted sets and stores result.\n * @param destination - Destination key\n * @param keys - Source keys\n * @param options - WEIGHTS and AGGREGATE options\n * @returns Number of elements in result\n */\n zunionstore(destination: string, keys: string[], options?: IZStoreOptions): Promise<number>;\n\n /**\n * Computes intersection of sorted sets and stores result.\n * @param destination - Destination key\n * @param keys - Source keys\n * @param options - WEIGHTS and AGGREGATE options\n * @returns Number of elements in result\n */\n zinterstore(destination: string, keys: string[], options?: IZStoreOptions): Promise<number>;\n\n /**\n * Gets scores of multiple members.\n * @returns Array of scores (null for non-existent members)\n */\n zmscore(key: string, ...members: string[]): Promise<Array<string | null>>;\n\n /**\n * Gets random members from sorted set.\n * @param count - Number of members (negative for duplicates allowed)\n * @param withScores - Include scores\n * @returns Random member(s) or member-score pairs\n */\n zrandmember(key: string, count?: number, withScores?: boolean): Promise<string | string[] | null>;\n\n /**\n * Gets members in score range in reverse order.\n * @returns Array of members (with scores if withScores)\n */\n zrevrangebyscore(key: string, max: number | string, min: number | string, options?: IZRangeByScoreOptions): Promise<string[]>;\n\n /**\n * Publishes message to channel.\n * @returns Number of subscribers that received the message\n */\n publish(channel: string, message: string): Promise<number>;\n\n /**\n * Subscribes to channels.\n */\n subscribe(...channels: string[]): Promise<void>;\n\n /**\n * Unsubscribes from channels.\n */\n unsubscribe(...channels: string[]): Promise<void>;\n\n /**\n * Subscribes to channels by pattern.\n */\n psubscribe(...patterns: string[]): Promise<void>;\n\n /**\n * Unsubscribes from channel patterns.\n */\n punsubscribe(...patterns: string[]): Promise<void>;\n\n /**\n * Adds entry to stream.\n * @param key - Stream key\n * @param id - Entry ID ('*' for auto-generated)\n * @param fields - Field-value pairs\n * @param options - Optional MAXLEN/MINID trimming\n * @returns Entry ID\n */\n xadd(key: string, id: string, fields: Record<string, string>, options?: IStreamAddOptions): Promise<string>;\n\n /**\n * Reads entries from streams.\n * @param streams - Stream keys and IDs to read from\n * @param options - Read options (COUNT, BLOCK)\n * @returns Array of stream entries or null\n */\n xread(streams: Array<{ key: string; id: string }>, options?: IStreamReadOptions): Promise<StreamReadResult | null>;\n\n /**\n * Reads entries from streams using consumer group.\n * @param group - Consumer group name\n * @param consumer - Consumer name\n * @param streams - Stream keys and IDs to read from\n * @param options - Read options (COUNT, BLOCK, NOACK)\n * @returns Array of stream entries or null\n */\n xreadgroup(group: string, consumer: string, streams: Array<{ key: string; id: string }>, options?: IStreamReadGroupOptions): Promise<StreamReadResult | null>;\n\n /**\n * Gets range of entries from stream.\n * @param key - Stream key\n * @param start - Start ID ('-' for oldest)\n * @param end - End ID ('+' for newest)\n * @param options - Optional COUNT\n * @returns Array of entries\n */\n xrange(key: string, start: string, end: string, options?: { count?: number }): Promise<IStreamEntry[]>;\n\n /**\n * Gets range of entries in reverse order.\n * @param key - Stream key\n * @param end - End ID ('+' for newest)\n * @param start - Start ID ('-' for oldest)\n * @param options - Optional COUNT\n * @returns Array of entries\n */\n xrevrange(key: string, end: string, start: string, options?: { count?: number }): Promise<IStreamEntry[]>;\n\n /**\n * Gets stream length.\n * @param key - Stream key\n * @returns Number of entries\n */\n xlen(key: string): Promise<number>;\n\n /**\n * Gets stream information.\n * @param key - Stream key\n * @returns Stream info object\n */\n xinfo(key: string): Promise<IStreamInfo>;\n\n /**\n * Trims stream to specified length.\n * @param key - Stream key\n * @param maxLen - Maximum length\n * @param approximate - Use '~' for approximate trimming (better performance)\n * @returns Number of entries removed\n */\n xtrim(key: string, maxLen: number, approximate?: boolean): Promise<number>;\n\n /**\n * Creates consumer group.\n * @param key - Stream key\n * @param group - Group name\n * @param id - Start ID ('$' for new entries only, '0' for all)\n * @param mkstream - Create stream if doesn't exist\n * @returns 'OK' if successful\n */\n xgroupCreate(key: string, group: string, id: string, mkstream?: boolean): Promise<'OK'>;\n\n /**\n * Destroys consumer group.\n * @param key - Stream key\n * @param group - Group name\n * @returns 1 if destroyed, 0 if didn't exist\n */\n xgroupDestroy(key: string, group: string): Promise<number>;\n\n /**\n * Deletes consumer from group.\n * @param key - Stream key\n * @param group - Group name\n * @param consumer - Consumer name\n * @returns Number of pending messages removed\n */\n xgroupDelConsumer(key: string, group: string, consumer: string): Promise<number>;\n\n /**\n * Sets consumer group last delivered ID.\n * @param key - Stream key\n * @param group - Group name\n * @param id - New last ID\n * @returns 'OK' if successful\n */\n xgroupSetId(key: string, group: string, id: string): Promise<'OK'>;\n\n /**\n * Acknowledges message processing.\n * @param key - Stream key\n * @param group - Group name\n * @param ids - Message IDs to acknowledge\n * @returns Number of messages acknowledged\n */\n xack(key: string, group: string, ...ids: string[]): Promise<number>;\n\n /**\n * Gets pending messages info.\n * @param key - Stream key\n * @param group - Group name\n * @returns Pending info summary\n */\n xpending(key: string, group: string): Promise<IStreamPendingInfo>;\n\n /**\n * Gets detailed pending messages.\n * @param key - Stream key\n * @param group - Group name\n * @param start - Start ID\n * @param end - End ID\n * @param count - Max entries to return\n * @param consumer - Optional consumer filter\n * @returns Array of pending entries\n */\n xpendingRange(key: string, group: string, start: string, end: string, count: number, consumer?: string): Promise<IStreamPendingEntry[]>;\n\n /**\n * Claims messages from another consumer.\n * @param key - Stream key\n * @param group - Group name\n * @param consumer - New consumer\n * @param minIdleTime - Minimum idle time in ms\n * @param ids - Message IDs to claim\n * @returns Claimed entries\n */\n xclaim(key: string, group: string, consumer: string, minIdleTime: number, ...ids: string[]): Promise<IStreamEntry[]>;\n\n /**\n * Deletes entries from stream.\n * @param key - Stream key\n * @param ids - Entry IDs to delete\n * @returns Number of entries deleted\n */\n xdel(key: string, ...ids: string[]): Promise<number>;\n\n /**\n * Creates a pipeline for batching commands.\n * Commands are executed atomically but not transactionally.\n */\n pipeline(): IPipeline;\n\n /**\n * Creates a multi/exec transaction.\n * Commands are executed atomically and transactionally.\n */\n multi(): IMulti;\n\n /**\n * Evaluates Lua script.\n * @param script - Lua script source\n * @param keys - KEYS array\n * @param args - ARGV array\n * @returns Script result\n */\n eval(script: string, keys: string[], args: Array<string | number>): Promise<unknown>;\n\n /**\n * Evaluates Lua script by SHA.\n * @param sha - Script SHA hash\n * @param keys - KEYS array\n * @param args - ARGV array\n * @returns Script result\n */\n evalsha(sha: string, keys: string[], args: Array<string | number>): Promise<unknown>;\n\n /**\n * Loads Lua script and returns SHA.\n * @param script - Lua script source\n * @returns SHA hash\n */\n scriptLoad(script: string): Promise<string>;\n\n /**\n * Checks if scripts exist.\n * @param shas - Script SHA hashes\n * @returns Array of 1/0 for each script\n */\n scriptExists(...shas: string[]): Promise<number[]>;\n\n /**\n * Flushes all scripts from cache.\n * @returns 'OK' if successful\n */\n scriptFlush(): Promise<'OK'>;\n\n /**\n * Flushes all keys from current database.\n * @returns 'OK' if successful\n */\n flushdb(): Promise<'OK'>;\n\n /**\n * Flushes all keys from all databases.\n * @returns 'OK' if successful\n */\n flushall(): Promise<'OK'>;\n\n /**\n * Gets server info.\n * @param section - Optional info section\n * @returns Info string\n */\n info(section?: string): Promise<string>;\n\n /**\n * Gets database size (number of keys).\n * @returns Number of keys\n */\n dbsize(): Promise<number>;\n\n /**\n * Adds elements to HyperLogLog.\n * @returns 1 if cardinality changed, 0 otherwise\n */\n pfadd(key: string, ...elements: string[]): Promise<number>;\n\n /**\n * Returns estimated cardinality.\n * @returns Estimated number of unique elements\n */\n pfcount(...keys: string[]): Promise<number>;\n\n /**\n * Merges HyperLogLogs into destination.\n * @returns 'OK' if successful\n */\n pfmerge(destination: string, ...sources: string[]): Promise<'OK'>;\n\n /**\n * Adds geospatial members.\n * @param members - Array of [longitude, latitude, member]\n * @returns Number of members added\n */\n geoadd(key: string, ...members: Array<number | string>): Promise<number>;\n\n /**\n * Returns distance between two members.\n * @param unit - m, km, mi, ft\n * @returns Distance or null if member doesn't exist\n */\n geodist(key: string, member1: string, member2: string, unit?: GeoUnit): Promise<string | null>;\n\n /**\n * Returns geohash strings for members.\n * @returns Array of geohash strings (null for non-existent)\n */\n geohash(key: string, ...members: string[]): Promise<Array<string | null>>;\n\n /**\n * Returns positions of members.\n * @returns Array of [longitude, latitude] or null\n */\n geopos(key: string, ...members: string[]): Promise<Array<[string, string] | null>>;\n\n /**\n * Searches for members in radius.\n * @returns Array of members with optional distance/coordinates\n */\n geosearch(key: string, options: IGeoSearchOptions): Promise<Array<string | IGeoSearchResult>>;\n\n /**\n * Searches and stores result.\n * @returns Number of members in result\n */\n geosearchstore(destination: string, source: string, options: IGeoSearchOptions & { storedist?: boolean }): Promise<number>;\n\n /**\n * Sets bit at offset.\n * @returns Previous bit value\n */\n setbit(key: string, offset: number, value: 0 | 1): Promise<number>;\n\n /**\n * Gets bit at offset.\n * @returns Bit value (0 or 1)\n */\n getbit(key: string, offset: number): Promise<number>;\n\n /**\n * Counts set bits in range.\n * @param start - Start byte offset\n * @param end - End byte offset\n * @param mode - BYTE or BIT\n * @returns Number of set bits\n */\n bitcount(key: string, start?: number, end?: number, mode?: 'BYTE' | 'BIT'): Promise<number>;\n\n /**\n * Performs bitwise operations.\n * @param operation - AND, OR, XOR, NOT\n * @param destKey - Destination key\n * @param keys - Source keys (one for NOT)\n * @returns Size of result in bytes\n */\n bitop(operation: 'AND' | 'OR' | 'XOR' | 'NOT', destKey: string, ...keys: string[]): Promise<number>;\n\n /**\n * Finds first bit set to value.\n * @param bit - 0 or 1\n * @param start - Start byte offset\n * @param end - End byte offset\n * @param mode - BYTE or BIT\n * @returns Position of first bit, -1 if not found\n */\n bitpos(key: string, bit: 0 | 1, start?: number, end?: number, mode?: 'BYTE' | 'BIT'): Promise<number>;\n\n /**\n * Executes Redis Cluster commands.\n * @param subcommand - Cluster subcommand (INFO, NODES, etc.)\n * @param args - Additional arguments\n * @returns Command result\n */\n cluster(subcommand: string, ...args: Array<string | number>): Promise<unknown>;\n\n /**\n * Executes Redis Sentinel commands.\n * @param subcommand - Sentinel subcommand (masters, master, replicas, etc.)\n * @param args - Additional arguments\n * @returns Command result\n */\n sentinel(subcommand: string, ...args: Array<string | number>): Promise<unknown>;\n\n /**\n * Registers event handler.\n */\n on(event: DriverEvent, handler: DriverEventHandler): void;\n\n /**\n * Registers one-time event handler.\n */\n once(event: DriverEvent, handler: DriverEventHandler): void;\n\n /**\n * Unregisters event handler.\n */\n off(event: DriverEvent, handler: DriverEventHandler): void;\n\n /**\n * Removes all listeners for event.\n */\n removeAllListeners(event?: DriverEvent): void;\n}\n\n/**\n * Options for SET command.\n */\nexport interface ISetOptions {\n /**\n * Expiration in seconds (EX).\n */\n ex?: number;\n\n /**\n * Expiration in milliseconds (PX).\n */\n px?: number;\n\n /**\n * Expiration Unix timestamp in seconds (EXAT).\n */\n exat?: number;\n\n /**\n * Expiration Unix timestamp in milliseconds (PXAT).\n */\n pxat?: number;\n\n /**\n * Only set if key exists (XX).\n */\n xx?: boolean;\n\n /**\n * Only set if key doesn't exist (NX).\n */\n nx?: boolean;\n\n /**\n * Return previous value (GET).\n */\n get?: boolean;\n\n /**\n * Keep existing TTL (KEEPTTL).\n */\n keepttl?: boolean;\n}\n\n/**\n * Options for SCAN family commands.\n */\nexport interface IScanOptions {\n /**\n * Match pattern.\n */\n match?: string;\n\n /**\n * Count hint (not exact).\n */\n count?: number;\n\n /**\n * Type filter (for SCAN only).\n */\n type?: string;\n}\n\n/**\n * Pipeline for batching Redis commands.\n * Commands are buffered and sent to server in a single round-trip.\n */\nexport interface IPipeline {\n /**\n * Executes all queued commands.\n * @returns Array of [error, result] tuples for each command\n */\n exec(): Promise<Array<[Error | null, unknown]>>;\n\n // All driver methods can be chained\n get(key: string): this;\n set(key: string, value: string, options?: ISetOptions): this;\n del(...keys: string[]): this;\n mget(...keys: string[]): this;\n mset(data: Record<string, string>): this;\n expire(key: string, seconds: number): this;\n ttl(key: string): this;\n incr(key: string): this;\n incrby(key: string, increment: number): this;\n hget(key: string, field: string): this;\n hset(key: string, field: string, value: string): this;\n hmset(key: string, data: Record<string, string>): this;\n hgetall(key: string): this;\n lpush(key: string, ...values: string[]): this;\n rpush(key: string, ...values: string[]): this;\n sadd(key: string, ...members: string[]): this;\n srem(key: string, ...members: string[]): this;\n zadd(key: string, ...args: Array<number | string>): this;\n zrem(key: string, ...members: string[]): this;\n}\n\n/**\n * Multi/Exec transaction.\n * Commands are executed atomically and transactionally.\n */\nexport interface IMulti extends IPipeline {\n /**\n * Discards transaction.\n */\n discard(): void;\n}\n\n/**\n * Options for XADD command.\n */\nexport interface IStreamAddOptions {\n /**\n * Maximum stream length (MAXLEN).\n */\n maxLen?: number;\n\n /**\n * Use approximate trimming (~) for better performance.\n */\n approximate?: boolean;\n\n /**\n * Only add if stream exists (NOMKSTREAM).\n */\n noMkStream?: boolean;\n\n /**\n * Minimum ID for trimming (MINID).\n */\n minId?: string;\n}\n\n/**\n * Options for XREAD command.\n */\nexport interface IStreamReadOptions {\n /**\n * Maximum entries per stream.\n */\n count?: number;\n\n /**\n * Block timeout in milliseconds (0 = forever).\n */\n block?: number;\n}\n\n/**\n * Options for XREADGROUP command.\n */\nexport interface IStreamReadGroupOptions extends IStreamReadOptions {\n /**\n * Don't add to pending list.\n */\n noAck?: boolean;\n}\n\n/**\n * Single stream entry.\n */\nexport interface IStreamEntry {\n /**\n * Entry ID (timestamp-sequence).\n */\n id: string;\n\n /**\n * Entry fields.\n */\n fields: Record<string, string>;\n}\n\n/**\n * Result from XREAD/XREADGROUP.\n */\nexport type StreamReadResult = Array<{\n /**\n * Stream key.\n */\n key: string;\n\n /**\n * Entries read from stream.\n */\n entries: IStreamEntry[];\n}>;\n\n/**\n * Stream information from XINFO.\n */\nexport interface IStreamInfo {\n /**\n * Number of entries.\n */\n length: number;\n\n /**\n * Number of consumer groups.\n */\n groups: number;\n\n /**\n * First entry ID.\n */\n firstEntry: IStreamEntry | null;\n\n /**\n * Last entry ID.\n */\n lastEntry: IStreamEntry | null;\n\n /**\n * Last generated ID.\n */\n lastGeneratedId: string;\n\n /**\n * Radix tree keys.\n */\n radixTreeKeys: number;\n\n /**\n * Radix tree nodes.\n */\n radixTreeNodes: number;\n}\n\n/**\n * Pending messages summary from XPENDING.\n */\nexport interface IStreamPendingInfo {\n /**\n * Total pending count.\n */\n count: number;\n\n /**\n * Smallest pending ID.\n */\n minId: string | null;\n\n /**\n * Largest pending ID.\n */\n maxId: string | null;\n\n /**\n * Pending count per consumer.\n */\n consumers: Array<{ name: string; count: number }>;\n}\n\n/**\n * Single pending entry from XPENDING with range.\n */\nexport interface IStreamPendingEntry {\n /**\n * Entry ID.\n */\n id: string;\n\n /**\n * Consumer name.\n */\n consumer: string;\n\n /**\n * Idle time in milliseconds.\n */\n idleTime: number;\n\n /**\n * Delivery count.\n */\n deliveryCount: number;\n}\n\n/**\n * Driver events.\n */\nexport enum DriverEvent {\n CONNECT = 'connect',\n READY = 'ready',\n DISCONNECT = 'disconnect',\n CLOSE = 'close',\n ERROR = 'error',\n RECONNECTING = 'reconnecting',\n END = 'end',\n}\n\n/**\n * Driver event handler.\n */\nexport type DriverEventHandler = (data?: unknown) => void;\n\n/**\n * Options for COPY command.\n */\nexport interface ICopyOptions {\n /**\n * Target database number.\n */\n db?: number;\n\n /**\n * Replace existing key.\n */\n replace?: boolean;\n}\n\n/**\n * Options for RESTORE command.\n */\nexport interface IRestoreOptions {\n /**\n * Replace existing key.\n */\n replace?: boolean;\n\n /**\n * Absolute Unix timestamp (milliseconds) for expiration.\n */\n absttl?: boolean;\n\n /**\n * Eviction time in seconds.\n */\n idletime?: number;\n\n /**\n * Eviction frequency.\n */\n freq?: number;\n}\n\n/**\n * Options for LPOS command.\n */\nexport interface ILposOptions {\n /**\n * Search from Nth match.\n */\n rank?: number;\n\n /**\n * Return count matches.\n */\n count?: number;\n\n /**\n * Limit comparisons.\n */\n maxlen?: number;\n}\n\n/**\n * Options for ZRANGEBYSCORE command.\n */\nexport interface IZRangeByScoreOptions {\n /**\n * Include scores in result.\n */\n withScores?: boolean;\n\n /**\n * Limit results.\n */\n limit?: {\n offset: number;\n count: number;\n };\n}\n\n/**\n * Options for ZUNIONSTORE/ZINTERSTORE commands.\n */\nexport interface IZStoreOptions {\n /**\n * Weight for each input key.\n */\n weights?: number[];\n\n /**\n * How to aggregate scores.\n */\n aggregate?: 'SUM' | 'MIN' | 'MAX';\n}\n\n/**\n * Geo distance unit.\n */\nexport type GeoUnit = 'm' | 'km' | 'mi' | 'ft';\n\n/**\n * Options for GEOSEARCH command.\n */\nexport interface IGeoSearchOptions {\n /**\n * Search from member.\n */\n member?: string;\n\n /**\n * Search from coordinates.\n */\n coord?: {\n longitude: number;\n latitude: number;\n };\n\n /**\n * Search by radius.\n */\n radius?: {\n value: number;\n unit: GeoUnit;\n };\n\n /**\n * Search by box.\n */\n box?: {\n width: number;\n height: number;\n unit: GeoUnit;\n };\n\n /**\n * Sort order.\n */\n sort?: 'ASC' | 'DESC';\n\n /**\n * Limit results.\n */\n count?: number;\n\n /**\n * Use ANY with count.\n */\n any?: boolean;\n\n /**\n * Include coordinates.\n */\n withCoord?: boolean;\n\n /**\n * Include distance.\n */\n withDist?: boolean;\n\n /**\n * Include hash.\n */\n withHash?: boolean;\n}\n\n/**\n * Geo search result with optional data.\n */\nexport interface IGeoSearchResult {\n member: string;\n distance?: string;\n hash?: number;\n coordinates?: [string, string];\n}\n","/**\n * All possible error codes in NestJS RedisX.\n * Organized by domain for easier categorization and handling.\n *\n * Naming convention: {DOMAIN}_{SPECIFIC_ERROR}\n *\n * Domains:\n * - CONN/DRIVER: Connection and networking errors\n * - OP: Redis operation errors\n * - CFG/CONFIG: Configuration and validation errors\n * - LOCK: Distributed lock errors\n * - CACHE: Caching errors\n * - RATE: Rate limiting errors\n * - STREAM: Stream processing errors\n * - PLUGIN: Plugin system errors\n * - IDEMPOTENCY: Idempotency errors\n */\nexport enum ErrorCode {\n /** Failed to establish connection to Redis server */\n CONN_FAILED = 'CONN_FAILED',\n\n /** Connection attempt timed out */\n CONN_TIMEOUT = 'CONN_TIMEOUT',\n\n /** Lost connection to Redis server */\n CONN_DISCONNECTED = 'CONN_DISCONNECTED',\n\n /** Connection was refused by Redis server */\n CONN_REFUSED = 'CONN_REFUSED',\n\n /** Authentication failed */\n CONN_AUTH_FAILED = 'CONN_AUTH_FAILED',\n\n /** TLS/SSL connection error */\n CONN_TLS_ERROR = 'CONN_TLS_ERROR',\n\n /** Redis cluster is down or unreachable */\n CONN_CLUSTER_DOWN = 'CONN_CLUSTER_DOWN',\n\n /** Redis cluster slot migration in progress */\n CONN_CLUSTER_MOVED = 'CONN_CLUSTER_MOVED',\n\n /** Redis cluster ASK redirection */\n CONN_CLUSTER_ASK = 'CONN_CLUSTER_ASK',\n\n /** Redis cluster error */\n CONN_CLUSTER_ERROR = 'CONN_CLUSTER_ERROR',\n\n /** Sentinel failover in progress */\n CONN_SENTINEL_FAILOVER = 'CONN_SENTINEL_FAILOVER',\n\n /** No Sentinel master found */\n CONN_SENTINEL_NO_MASTER = 'CONN_SENTINEL_NO_MASTER',\n\n /** Sentinel error */\n CONN_SENTINEL_ERROR = 'CONN_SENTINEL_ERROR',\n\n /** Driver not connected */\n DRIVER_NOT_CONNECTED = 'DRIVER_NOT_CONNECTED',\n\n /** Maximum connection retries exceeded */\n CONN_MAX_RETRIES = 'CONN_MAX_RETRIES',\n\n /** Connection pool exhausted */\n CONN_POOL_EXHAUSTED = 'CONN_POOL_EXHAUSTED',\n\n /** Redis operation failed */\n OP_FAILED = 'OP_FAILED',\n\n /** Redis operation timed out */\n OP_TIMEOUT = 'OP_TIMEOUT',\n\n /** Redis key not found */\n OP_KEY_NOT_FOUND = 'OP_KEY_NOT_FOUND',\n\n /** Wrong Redis data type for operation */\n OP_TYPE_MISMATCH = 'OP_TYPE_MISMATCH',\n\n /** Redis script execution failed */\n OP_SCRIPT_ERROR = 'OP_SCRIPT_ERROR',\n\n /** Redis script timeout */\n OP_SCRIPT_TIMEOUT = 'OP_SCRIPT_TIMEOUT',\n\n /** Redis transaction failed */\n OP_TRANSACTION_FAILED = 'OP_TRANSACTION_FAILED',\n\n /** Redis pipeline execution failed */\n OP_PIPELINE_FAILED = 'OP_PIPELINE_FAILED',\n\n /** Invalid operation arguments */\n OP_INVALID_ARGS = 'OP_INVALID_ARGS',\n\n /** Operation not supported by driver */\n OP_NOT_SUPPORTED = 'OP_NOT_SUPPORTED',\n\n /** Redis out of memory */\n OP_OUT_OF_MEMORY = 'OP_OUT_OF_MEMORY',\n\n /** Redis NOAUTH error - authentication required */\n OP_NO_AUTH = 'OP_NO_AUTH',\n\n /** Redis WRONGPASS error - invalid password */\n OP_WRONG_PASS = 'OP_WRONG_PASS',\n\n /** Redis READONLY error - can't write to replica */\n OP_READONLY = 'OP_READONLY',\n\n /** Redis BUSYKEY error - target key already exists */\n OP_BUSY_KEY = 'OP_BUSY_KEY',\n\n /** Operation requires driver to be connected */\n OP_NOT_CONNECTED = 'OP_NOT_CONNECTED',\n\n /** Invalid configuration */\n CFG_INVALID = 'CFG_INVALID',\n\n /** Missing required configuration */\n CFG_MISSING_REQUIRED = 'CFG_MISSING_REQUIRED',\n\n /** Invalid connection type */\n CFG_INVALID_CONNECTION_TYPE = 'CFG_INVALID_CONNECTION_TYPE',\n\n /** Invalid host or port */\n CFG_INVALID_HOST_PORT = 'CFG_INVALID_HOST_PORT',\n\n /** Invalid database number */\n CFG_INVALID_DB = 'CFG_INVALID_DB',\n\n /** Invalid TTL value */\n CFG_INVALID_TTL = 'CFG_INVALID_TTL',\n\n /** Invalid timeout value */\n CFG_INVALID_TIMEOUT = 'CFG_INVALID_TIMEOUT',\n\n /** Invalid retry configuration */\n CFG_INVALID_RETRY = 'CFG_INVALID_RETRY',\n\n /** Validation failed */\n CFG_VALIDATION_FAILED = 'CFG_VALIDATION_FAILED',\n\n /** Incompatible configuration options */\n CFG_INCOMPATIBLE = 'CFG_INCOMPATIBLE',\n\n /** Driver type not supported */\n CFG_DRIVER_NOT_SUPPORTED = 'CFG_DRIVER_NOT_SUPPORTED',\n\n /** Invalid cluster nodes configuration */\n CFG_INVALID_CLUSTER_NODES = 'CFG_INVALID_CLUSTER_NODES',\n\n /** Invalid sentinel configuration */\n CFG_INVALID_SENTINEL = 'CFG_INVALID_SENTINEL',\n\n /** Invalid TLS configuration */\n CFG_INVALID_TLS = 'CFG_INVALID_TLS',\n\n /** Cache key is invalid */\n CACHE_KEY_INVALID = 'CACHE_KEY_INVALID',\n\n /** Cache key exceeds maximum length */\n CACHE_KEY_TOO_LONG = 'CACHE_KEY_TOO_LONG',\n\n /** Failed to serialize value */\n CACHE_SERIALIZATION_FAILED = 'CACHE_SERIALIZATION_FAILED',\n\n /** Failed to deserialize value */\n CACHE_DESERIALIZATION_FAILED = 'CACHE_DESERIALIZATION_FAILED',\n\n /** Cache tag index corrupted */\n CACHE_TAG_INDEX_CORRUPTED = 'CACHE_TAG_INDEX_CORRUPTED',\n\n /** Cache stampede protection timeout */\n CACHE_STAMPEDE_TIMEOUT = 'CACHE_STAMPEDE_TIMEOUT',\n\n /** Cache loader function failed */\n CACHE_LOADER_FAILED = 'CACHE_LOADER_FAILED',\n\n /** L1 cache error */\n CACHE_L1_ERROR = 'CACHE_L1_ERROR',\n\n /** L2 cache error */\n CACHE_L2_ERROR = 'CACHE_L2_ERROR',\n\n /** Cache set operation failed */\n CACHE_SET_FAILED = 'CACHE_SET_FAILED',\n\n /** Cache delete operation failed */\n CACHE_DELETE_FAILED = 'CACHE_DELETE_FAILED',\n\n /** Cache clear operation failed */\n CACHE_CLEAR_FAILED = 'CACHE_CLEAR_FAILED',\n\n /** Cache operation failed */\n CACHE_OPERATION_FAILED = 'CACHE_OPERATION_FAILED',\n\n /** Cache operation timeout */\n CACHE_OPERATION_TIMEOUT = 'CACHE_OPERATION_TIMEOUT',\n\n /** Tag invalidation failed */\n CACHE_TAG_INVALIDATION_FAILED = 'CACHE_TAG_INVALIDATION_FAILED',\n\n /** Failed to acquire lock */\n LOCK_ACQUISITION_FAILED = 'LOCK_ACQUISITION_FAILED',\n\n /** Lock acquisition timed out */\n LOCK_ACQUISITION_TIMEOUT = 'LOCK_ACQUISITION_TIMEOUT',\n\n /** Failed to extend lock TTL */\n LOCK_EXTENSION_FAILED = 'LOCK_EXTENSION_FAILED',\n\n /** Failed to release lock */\n LOCK_RELEASE_FAILED = 'LOCK_RELEASE_FAILED',\n\n /** Attempting to release lock not owned by caller */\n LOCK_NOT_OWNED = 'LOCK_NOT_OWNED',\n\n /** Lock expired before operation completed */\n LOCK_EXPIRED = 'LOCK_EXPIRED',\n\n /** Rate limit exceeded */\n RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED',\n\n /** Rate limit script error */\n RATE_LIMIT_SCRIPT_ERROR = 'RATE_LIMIT_SCRIPT_ERROR',\n\n /** Idempotency key is invalid */\n IDEMPOTENCY_KEY_INVALID = 'IDEMPOTENCY_KEY_INVALID',\n\n /** Operation already in progress */\n IDEMPOTENCY_IN_PROGRESS = 'IDEMPOTENCY_IN_PROGRESS',\n\n /** Previous operation failed */\n IDEMPOTENCY_PREVIOUS_FAILED = 'IDEMPOTENCY_PREVIOUS_FAILED',\n\n /** Consumer group operation failed */\n STREAM_CONSUMER_GROUP_ERROR = 'STREAM_CONSUMER_GROUP_ERROR',\n\n /** Stream ACK failed */\n STREAM_ACK_FAILED = 'STREAM_ACK_FAILED',\n\n /** Stream read failed */\n STREAM_READ_FAILED = 'STREAM_READ_FAILED',\n\n /** Plugin is invalid */\n PLUGIN_INVALID = 'PLUGIN_INVALID',\n\n /** Plugin already registered */\n PLUGIN_DUPLICATE = 'PLUGIN_DUPLICATE',\n\n /** Plugin not found */\n PLUGIN_NOT_FOUND = 'PLUGIN_NOT_FOUND',\n\n /** Plugin registration failed */\n PLUGIN_REGISTER_FAILED = 'PLUGIN_REGISTER_FAILED',\n\n /** Plugin initialization failed */\n PLUGIN_INIT_FAILED = 'PLUGIN_INIT_FAILED',\n\n /** Circular plugin dependency */\n PLUGIN_CIRCULAR_DEPENDENCY = 'PLUGIN_CIRCULAR_DEPENDENCY',\n\n /** Required plugin dependency missing */\n PLUGIN_DEPENDENCY_MISSING = 'PLUGIN_DEPENDENCY_MISSING',\n\n /** Client not found */\n CLIENT_NOT_FOUND = 'CLIENT_NOT_FOUND',\n\n /** Client already exists */\n CLIENT_ALREADY_EXISTS = 'CLIENT_ALREADY_EXISTS',\n\n /** Service not initialized */\n NOT_INITIALIZED = 'NOT_INITIALIZED',\n\n /** Generic operation failed */\n OPERATION_FAILED = 'OPERATION_FAILED',\n\n /** Generic operation timeout */\n OPERATION_TIMEOUT = 'OPERATION_TIMEOUT',\n\n /** Generic serialization failed */\n SERIALIZATION_FAILED = 'SERIALIZATION_FAILED',\n\n /** Generic validation failed */\n VALIDATION_FAILED = 'VALIDATION_FAILED',\n\n /** Unknown error */\n UNKNOWN = 'UNKNOWN',\n}\n\n/**\n * Type guard to check if a string is a valid ErrorCode.\n */\nexport function isErrorCode(value: string): value is ErrorCode {\n return Object.values(ErrorCode).includes(value as ErrorCode);\n}\n\n/**\n * Get error domain from error code.\n *\n * @param code - Error code\n * @returns Domain prefix (e.g., 'CONN', 'OP', 'CFG')\n *\n * @example\n * ```typescript\n * getErrorDomain(ErrorCode.CONN_FAILED) // 'CONN'\n * getErrorDomain(ErrorCode.OP_TIMEOUT) // 'OP'\n * ```\n */\nexport function getErrorDomain(code: ErrorCode): string {\n const parts = code.split('_');\n return parts[0] || 'UNKNOWN';\n}\n\n/**\n * Check if error code belongs to a domain.\n *\n * @param code - Error code to check\n * @param domain - Domain to check against\n * @returns True if code belongs to domain\n *\n * @example\n * ```typescript\n * isErrorDomain(ErrorCode.CONN_FAILED, 'CONN') // true\n * isErrorDomain(ErrorCode.OP_TIMEOUT, 'CONN') // false\n * ```\n */\nexport function isErrorDomain(code: ErrorCode, domain: string): boolean {\n return getErrorDomain(code) === domain;\n}\n","import { ErrorCode } from './error-codes';\n\n/**\n * JSON representation of an error.\n * Used for serialization in logs and API responses.\n */\nexport interface IErrorJSON {\n /** Error class name */\n name: string;\n /** Human-readable error message */\n message: string;\n /** Error code for programmatic handling */\n code: string;\n /** ISO timestamp when error occurred */\n timestamp: string;\n /** Additional context for debugging */\n context?: Record<string, unknown>;\n /** Stack trace */\n stack?: string;\n /** Cause error if wrapped */\n cause?: {\n name: string;\n message: string;\n stack?: string;\n };\n}\n\n/**\n * Base error class for all RedisX errors.\n *\n * Features:\n * - Structured error codes for programmatic handling\n * - Context preservation for debugging\n * - JSON serialization for logging\n * - Proper error chaining with cause\n * - Timestamp tracking\n *\n * While this class can be instantiated directly, it's recommended\n * to use or extend specific error classes for better error handling.\n *\n * @example\n * ```typescript\n * export class CacheError extends RedisXError {\n * constructor(message: string, cause?: Error) {\n * super(message, ErrorCode.CACHE_LOADER_FAILED, cause, {\n * key: 'user:123',\n * ttl: 3600,\n * });\n * }\n * }\n * ```\n */\nexport class RedisXError extends Error {\n /**\n * Timestamp when error was created.\n */\n public readonly timestamp: Date;\n\n /**\n * Creates a new RedisXError.\n *\n * @param message - Human-readable error message\n * @param code - Error code for programmatic handling\n * @param cause - Original error that caused this error\n * @param context - Additional context for debugging\n */\n constructor(\n message: string,\n public readonly code: ErrorCode,\n public override readonly cause?: Error,\n public readonly context?: Record<string, unknown>,\n ) {\n super(message);\n\n // Set the prototype explicitly (TypeScript requirement)\n Object.setPrototypeOf(this, new.target.prototype);\n\n this.name = this.constructor.name;\n this.timestamp = new Date();\n\n // Capture stack trace, excluding constructor\n Error.captureStackTrace(this, this.constructor);\n }\n\n /**\n * Checks if error is of specific code.\n *\n * @param code - Error code to check\n * @returns True if error matches code\n *\n * @example\n * ```typescript\n * if (error.is(ErrorCode.CONN_FAILED)) {\n * // Handle connection error\n * }\n * ```\n */\n is(code: ErrorCode): boolean {\n return this.code === code;\n }\n\n /**\n * Checks if error is any of specified codes.\n *\n * @param codes - Array of error codes to check\n * @returns True if error matches any code\n *\n * @example\n * ```typescript\n * if (error.isAnyOf([ErrorCode.LOCK_EXPIRED, ErrorCode.LOCK_NOT_OWNED])) {\n * // Handle lock error\n * }\n * ```\n */\n isAnyOf(codes: ErrorCode[]): boolean {\n return codes.includes(this.code);\n }\n\n /**\n * Serializes error to JSON for logging.\n *\n * @returns JSON representation of error\n *\n * @example\n * ```typescript\n * logger.error('Operation failed', error.toJSON());\n * ```\n */\n toJSON(): IErrorJSON {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n timestamp: this.timestamp.toISOString(),\n context: this.context,\n cause: this.cause\n ? {\n name: this.cause.name,\n message: this.cause.message,\n stack: this.cause.stack,\n }\n : undefined,\n stack: this.stack,\n };\n }\n\n /**\n * Creates error string for logging.\n *\n * @returns Formatted error string\n */\n override toString(): string {\n let str = `${this.name} [${this.code}]: ${this.message}`;\n\n if (this.cause) {\n str += `\\n Caused by: ${this.cause.message}`;\n }\n\n if (this.context && Object.keys(this.context).length > 0) {\n str += `\\n Context: ${JSON.stringify(this.context)}`;\n }\n\n return str;\n }\n\n /**\n * Wraps unknown error in RedisXError.\n *\n * @param error - Error to wrap\n * @param code - Error code to assign\n * @returns RedisXError instance\n *\n * @example\n * ```typescript\n * try {\n * await someOperation();\n * } catch (err) {\n * throw RedisXError.wrap(err, ErrorCode.CACHE_LOADER_FAILED);\n * }\n * ```\n */\n static wrap(error: unknown, code: ErrorCode = ErrorCode.UNKNOWN): RedisXError {\n if (error instanceof RedisXError) {\n return error;\n }\n\n if (error instanceof Error) {\n return new GenericRedisXError(error.message, code, error);\n }\n\n return new GenericRedisXError(String(error), code, undefined, { originalError: error });\n }\n\n /**\n * Checks if value is a RedisXError.\n *\n * @param error - Value to check\n * @returns True if value is RedisXError\n */\n static isRedisXError(error: unknown): error is RedisXError {\n return error instanceof RedisXError;\n }\n}\n\n/**\n * Generic RedisX error for wrapping unknown errors.\n * @internal\n */\nclass GenericRedisXError extends RedisXError {\n constructor(message: string, code: ErrorCode, cause?: Error, context?: Record<string, unknown>) {\n super(message, code, cause, context);\n }\n}\n","import { RedisXError } from './base.error';\nimport { ErrorCode } from './error-codes';\n\n/**\n * Base class for all connection-related errors.\n *\n * Provides common context for connection failures including\n * host, port, and connection type information.\n */\nexport class RedisConnectionError extends RedisXError {\n constructor(\n message: string,\n code: ErrorCode,\n public readonly host?: string,\n public readonly port?: number,\n cause?: Error,\n context?: Record<string, unknown>,\n ) {\n super(message, code, cause, {\n ...context,\n host,\n port,\n });\n }\n\n /**\n * Creates connection error with default code.\n */\n static create(message: string, host?: string, port?: number, cause?: Error): RedisConnectionError {\n return new RedisConnectionError(message, ErrorCode.CONN_FAILED, host, port, cause);\n }\n}\n\n/**\n * Error thrown when connection or operation times out.\n *\n * Includes timeout duration and operation details for debugging.\n *\n * @example\n * ```typescript\n * throw new RedisTimeoutError(\n * 'Connection',\n * 5000,\n * 'localhost',\n * 6379,\n * );\n * ```\n */\nexport class RedisTimeoutError extends RedisConnectionError {\n constructor(\n public readonly operation: string,\n public readonly timeoutMs: number,\n host?: string,\n port?: number,\n cause?: Error,\n ) {\n const message = operation === 'Connection' ? `Connection to ${host}:${port} timed out after ${timeoutMs}ms` : `Operation \"${operation}\" timed out after ${timeoutMs}ms`;\n\n super(message, operation === 'Connection' ? ErrorCode.CONN_TIMEOUT : ErrorCode.OP_TIMEOUT, host, port, cause, { operation, timeoutMs });\n }\n}\n\n/**\n * Error thrown for Redis Cluster-specific failures.\n *\n * Handles cluster slot redirections (MOVED, ASK), cluster down states,\n * and other cluster topology issues.\n *\n * @example\n * ```typescript\n * // Cluster down\n * throw new RedisClusterError(\n * 'Cluster is down',\n * ErrorCode.CONN_CLUSTER_DOWN,\n * );\n *\n * // Slot migration\n * throw new RedisClusterError(\n * 'Key moved to different slot',\n * ErrorCode.CONN_CLUSTER_MOVED,\n * undefined,\n * undefined,\n * undefined,\n * { slot: 1234, targetNode: 'node2:7001' },\n * );\n * ```\n */\nexport class RedisClusterError extends RedisConnectionError {\n constructor(message: string, code: ErrorCode, host?: string, port?: number, cause?: Error, context?: Record<string, unknown>) {\n super(message, code, host, port, cause, context);\n }\n\n /**\n * Creates cluster down error.\n */\n static clusterDown(host?: string, port?: number): RedisClusterError {\n return new RedisClusterError('Redis cluster is down or unreachable', ErrorCode.CONN_CLUSTER_DOWN, host, port);\n }\n\n /**\n * Creates MOVED redirection error.\n */\n static moved(slot: number, targetHost: string, targetPort: number, cause?: Error): RedisClusterError {\n return new RedisClusterError(`Slot ${slot} moved to ${targetHost}:${targetPort}`, ErrorCode.CONN_CLUSTER_MOVED, targetHost, targetPort, cause, { slot, targetHost, targetPort });\n }\n\n /**\n * Creates ASK redirection error.\n */\n static ask(slot: number, targetHost: string, targetPort: number, cause?: Error): RedisClusterError {\n return new RedisClusterError(`ASK redirection for slot ${slot} to ${targetHost}:${targetPort}`, ErrorCode.CONN_CLUSTER_ASK, targetHost, targetPort, cause, { slot, targetHost, targetPort });\n }\n\n /**\n * Creates generic cluster error.\n */\n static generic(message: string, host?: string, port?: number, cause?: Error): RedisClusterError {\n return new RedisClusterError(message, ErrorCode.CONN_CLUSTER_ERROR, host, port, cause);\n }\n}\n\n/**\n * Error thrown for Redis Sentinel-specific failures.\n *\n * Handles sentinel master discovery, failover events, and sentinel\n * connectivity issues.\n *\n * @example\n * ```typescript\n * // No master found\n * throw RedisSentinelError.noMaster('mymaster', [\n * { host: 'sentinel1', port: 26379 },\n * { host: 'sentinel2', port: 26379 },\n * ]);\n *\n * // Failover in progress\n * throw RedisSentinelError.failover(\n * 'mymaster',\n * 'old-master:6379',\n * 'new-master:6379',\n * );\n * ```\n */\nexport class RedisSentinelError extends RedisConnectionError {\n constructor(\n message: string,\n code: ErrorCode,\n public readonly masterName?: string,\n host?: string,\n port?: number,\n cause?: Error,\n context?: Record<string, unknown>,\n ) {\n super(message, code, host, port, cause, {\n ...context,\n masterName,\n });\n }\n\n /**\n * Creates no master found error.\n */\n static noMaster(masterName: string, sentinels?: Array<{ host: string; port: number }>): RedisSentinelError {\n return new RedisSentinelError(`No master found for \"${masterName}\"`, ErrorCode.CONN_SENTINEL_NO_MASTER, masterName, undefined, undefined, undefined, { sentinels });\n }\n\n /**\n * Creates sentinel failover error.\n */\n static failover(masterName: string, oldMaster?: string, newMaster?: string): RedisSentinelError {\n return new RedisSentinelError(`Sentinel failover in progress for \"${masterName}\"`, ErrorCode.CONN_SENTINEL_FAILOVER, masterName, undefined, undefined, undefined, { oldMaster, newMaster });\n }\n\n /**\n * Creates generic sentinel error.\n */\n static generic(message: string, masterName?: string, cause?: Error): RedisSentinelError {\n return new RedisSentinelError(message, ErrorCode.CONN_SENTINEL_ERROR, masterName, undefined, undefined, cause);\n }\n}\n\n/**\n * Error thrown when authentication fails.\n *\n * @example\n * ```typescript\n * throw new RedisAuthError('Invalid password', 'localhost', 6379);\n * ```\n */\nexport class RedisAuthError extends RedisConnectionError {\n constructor(message: string = 'Redis authentication failed', host?: string, port?: number, cause?: Error) {\n super(message, ErrorCode.CONN_AUTH_FAILED, host, port, cause);\n }\n}\n\n/**\n * Error thrown when TLS/SSL connection fails.\n *\n * @example\n * ```typescript\n * throw new RedisTLSError(\n * 'Certificate verification failed',\n * 'secure.redis.example.com',\n * 6380,\n * originalError,\n * );\n * ```\n */\nexport class RedisTLSError extends RedisConnectionError {\n constructor(message: string = 'TLS/SSL connection failed', host?: string, port?: number, cause?: Error) {\n super(message, ErrorCode.CONN_TLS_ERROR, host, port, cause);\n }\n}\n\n/**\n * Error thrown when maximum connection retries exceeded.\n *\n * @example\n * ```typescript\n * throw new RedisMaxRetriesError(\n * 5,\n * 'localhost',\n * 6379,\n * lastError,\n * );\n * ```\n */\nexport class RedisMaxRetriesError extends RedisConnectionError {\n constructor(\n public readonly maxRetries: number,\n host?: string,\n port?: number,\n cause?: Error,\n ) {\n super(`Maximum connection retries (${maxRetries}) exceeded`, ErrorCode.CONN_MAX_RETRIES, host, port, cause, { maxRetries });\n }\n}\n\n/**\n * Error thrown when connection pool is exhausted.\n *\n * @example\n * ```typescript\n * throw new RedisPoolExhaustedError(10, 100);\n * ```\n */\nexport class RedisPoolExhaustedError extends RedisConnectionError {\n constructor(\n public readonly poolSize: number,\n public readonly waitingClients: number,\n cause?: Error,\n ) {\n super(`Connection pool exhausted (size: ${poolSize}, waiting: ${waitingClients})`, ErrorCode.CONN_POOL_EXHAUSTED, undefined, undefined, cause, { poolSize, waitingClients });\n }\n}\n","import { RedisXError } from './base.error';\nimport { ErrorCode } from './error-codes';\n\n/**\n * Base class for all Redis operation errors.\n *\n * Provides common context for operation failures including\n * command name and arguments.\n */\nexport class RedisOperationError extends RedisXError {\n constructor(\n message: string,\n code: ErrorCode,\n public readonly command?: string,\n cause?: Error,\n context?: Record<string, unknown>,\n ) {\n super(message, code, cause, {\n ...context,\n command,\n });\n }\n\n /**\n * Creates operation error with default code.\n */\n static create(message: string, command?: string, cause?: Error): RedisOperationError {\n return new RedisOperationError(message, ErrorCode.OP_FAILED, command, cause);\n }\n\n /**\n * Creates operation error from command and arguments.\n */\n static fromCommand(command: string, args: unknown[], cause?: Error): RedisOperationError {\n return new RedisOperationError(`Command \"${command}\" failed: ${cause?.message ?? 'Unknown error'}`, ErrorCode.OP_FAILED, command, cause, { argsCount: args.length });\n }\n}\n\n/**\n * Error thrown when Redis key is not found.\n *\n * Used for operations that require a key to exist.\n *\n * @example\n * ```typescript\n * const value = await redis.get('user:123');\n * if (!value) {\n * throw new RedisKeyNotFoundError('user:123', 'GET');\n * }\n * ```\n */\nexport class RedisKeyNotFoundError extends RedisOperationError {\n constructor(\n public readonly key: string,\n command?: string,\n cause?: Error,\n ) {\n super(`Key \"${key}\" not found`, ErrorCode.OP_KEY_NOT_FOUND, command, cause, { key });\n }\n}\n\n/**\n * Error thrown when Redis value type doesn't match expected type.\n *\n * For example, trying to LPUSH to a string value.\n *\n * @example\n * ```typescript\n * // Trying to use list command on a string\n * throw new RedisTypeMismatchError(\n * 'mykey',\n * 'list',\n * 'string',\n * 'LPUSH',\n * );\n * ```\n */\nexport class RedisTypeMismatchError extends RedisOperationError {\n constructor(\n public readonly key: string,\n public readonly expected: string,\n public readonly actual: string,\n command?: string,\n cause?: Error,\n ) {\n super(`Type mismatch for key \"${key}\": expected ${expected}, got ${actual}`, ErrorCode.OP_TYPE_MISMATCH, command, cause, { key, expected, actual });\n }\n\n /**\n * Creates type mismatch from Redis WRONGTYPE error.\n */\n static fromRedisError(key: string, command: string, cause: Error): RedisTypeMismatchError {\n // Try to parse expected/actual types from error message\n const message = cause.message.toLowerCase();\n let expected = 'unknown';\n const actual = 'unknown';\n\n // Redis error format: \"WRONGTYPE Operation against a key holding the wrong kind of value\"\n if (message.includes('wrongtype')) {\n // Try to infer from command\n if (command.startsWith('L') || command.startsWith('R') || command.startsWith('BLPOP')) {\n expected = 'list';\n } else if (command.startsWith('S') && !command.startsWith('SET')) {\n expected = 'set';\n } else if (command.startsWith('Z')) {\n expected = 'zset';\n } else if (command.startsWith('H')) {\n expected = 'hash';\n }\n }\n\n return new RedisTypeMismatchError(key, expected, actual, command, cause);\n }\n}\n\n/**\n * Error thrown when Redis Lua script execution fails.\n *\n * Includes script SHA and error details for debugging.\n *\n * @example\n * ```typescript\n * throw new RedisScriptError(\n * 'Script execution failed: division by zero',\n * 'abc123...',\n * originalError,\n * { line: 42 },\n * );\n * ```\n */\nexport class RedisScriptError extends RedisOperationError {\n constructor(\n message: string,\n public readonly scriptSha?: string,\n cause?: Error,\n context?: Record<string, unknown>,\n ) {\n super(message, ErrorCode.OP_SCRIPT_ERROR, 'EVALSHA', cause, {\n ...context,\n scriptSha,\n });\n }\n\n /**\n * Creates script timeout error.\n */\n static timeout(timeoutMs: number, scriptSha?: string): RedisScriptError {\n return new RedisScriptError(`Script execution timed out after ${timeoutMs}ms`, scriptSha, undefined, { timeoutMs });\n }\n\n /**\n * Creates script not found error (NOSCRIPT).\n */\n static notFound(scriptSha: string): RedisScriptError {\n return new RedisScriptError(`Script not found in Redis cache: ${scriptSha}`, scriptSha, undefined, { reason: 'NOSCRIPT' });\n }\n}\n\n/**\n * Error thrown when Redis transaction (MULTI/EXEC) fails.\n *\n * @example\n * ```typescript\n * throw new RedisTransactionError(\n * 'Transaction aborted due to WATCH key modification',\n * originalError,\n * );\n * ```\n */\nexport class RedisTransactionError extends RedisOperationError {\n constructor(message: string, cause?: Error, context?: Record<string, unknown>) {\n super(message, ErrorCode.OP_TRANSACTION_FAILED, 'EXEC', cause, context);\n }\n\n /**\n * Creates transaction aborted error (WATCH failure).\n */\n static aborted(): RedisTransactionError {\n return new RedisTransactionError('Transaction aborted: watched key was modified', undefined, { reason: 'WATCH' });\n }\n}\n\n/**\n * Error thrown when Redis pipeline execution fails.\n *\n * @example\n * ```typescript\n * throw new RedisPipelineError(\n * 'Pipeline execution failed: 3 of 10 commands failed',\n * 10,\n * 3,\n * originalError,\n * );\n * ```\n */\nexport class RedisPipelineError extends RedisOperationError {\n constructor(\n message: string,\n public readonly totalCommands: number,\n public readonly failedCommands: number,\n cause?: Error,\n ) {\n super(message, ErrorCode.OP_PIPELINE_FAILED, 'PIPELINE', cause, { totalCommands, failedCommands });\n }\n}\n\n/**\n * Error thrown when operation has invalid arguments.\n *\n * @example\n * ```typescript\n * throw new RedisInvalidArgsError(\n * 'SET',\n * 'TTL must be a positive number',\n * { ttl: -1 },\n * );\n * ```\n */\nexport class RedisInvalidArgsError extends RedisOperationError {\n constructor(\n command: string,\n reason: string,\n public readonly args?: Record<string, unknown>,\n ) {\n super(`Invalid arguments for ${command}: ${reason}`, ErrorCode.OP_INVALID_ARGS, command, undefined, { args, reason });\n }\n}\n\n/**\n * Error thrown when Redis returns out of memory error.\n *\n * @example\n * ```typescript\n * throw new RedisOutOfMemoryError('SET', 'user:123');\n * ```\n */\nexport class RedisOutOfMemoryError extends RedisOperationError {\n constructor(\n command: string,\n public readonly key?: string,\n cause?: Error,\n ) {\n super(`Redis out of memory while executing ${command}${key ? ` on key \"${key}\"` : ''}`, ErrorCode.OP_OUT_OF_MEMORY, command, cause, { key });\n }\n}\n\n/**\n * Error thrown when attempting to write to a read-only replica.\n *\n * @example\n * ```typescript\n * throw new RedisReadOnlyError('SET', 'user:123');\n * ```\n */\nexport class RedisReadOnlyError extends RedisOperationError {\n constructor(\n command: string,\n public readonly key?: string,\n cause?: Error,\n ) {\n super(`Cannot write to read-only replica: ${command}${key ? ` (key: ${key})` : ''}`, ErrorCode.OP_READONLY, command, cause, { key });\n }\n}\n\n/**\n * Error thrown when operation is not supported by current driver.\n *\n * @example\n * ```typescript\n * throw new RedisNotSupportedError(\n * 'GEOSEARCH',\n * 'Requires Redis 6.2+',\n * );\n * ```\n */\nexport class RedisNotSupportedError extends RedisOperationError {\n constructor(\n command: string,\n public readonly reason: string,\n ) {\n super(`Command ${command} not supported: ${reason}`, ErrorCode.OP_NOT_SUPPORTED, command, undefined, { reason });\n }\n}\n\n/**\n * Error thrown when driver is not connected.\n *\n * @example\n * ```typescript\n * if (!this.isConnected()) {\n * throw new RedisNotConnectedError('GET');\n * }\n * ```\n */\nexport class RedisNotConnectedError extends RedisOperationError {\n constructor(command?: string) {\n super(`Cannot execute ${command ?? 'operation'}: driver not connected`, ErrorCode.OP_NOT_CONNECTED, command);\n }\n}\n","import { RedisXError } from './base.error';\nimport { ErrorCode } from './error-codes';\n\n/**\n * Base class for all configuration errors.\n *\n * Thrown during module initialization when configuration is invalid.\n */\nexport class RedisConfigError extends RedisXError {\n constructor(message: string, code: ErrorCode, cause?: Error, context?: Record<string, unknown>) {\n super(message, code, cause, context);\n }\n\n /**\n * Creates generic config error.\n */\n static create(message: string, cause?: Error, context?: Record<string, unknown>): RedisConfigError {\n return new RedisConfigError(message, ErrorCode.CFG_INVALID, cause, context);\n }\n\n /**\n * Creates missing required field error.\n */\n static missingRequired(field: string, parent?: string): RedisConfigError {\n const path = parent ? `${parent}.${field}` : field;\n return new RedisConfigError(`Missing required configuration field: ${path}`, ErrorCode.CFG_MISSING_REQUIRED, undefined, { field, parent, path });\n }\n\n /**\n * Creates invalid connection type error.\n */\n static invalidConnectionType(type: string, validTypes: string[]): RedisConfigError {\n return new RedisConfigError(`Invalid connection type \"${type}\". Must be one of: ${validTypes.join(', ')}`, ErrorCode.CFG_INVALID_CONNECTION_TYPE, undefined, { type, validTypes });\n }\n\n /**\n * Creates invalid host/port error.\n */\n static invalidHostPort(host?: string, port?: number, reason?: string): RedisConfigError {\n const message = reason ? `Invalid host/port configuration: ${reason}` : `Invalid host \"${host}\" or port \"${port}\"`;\n\n return new RedisConfigError(message, ErrorCode.CFG_INVALID_HOST_PORT, undefined, { host, port, reason });\n }\n\n /**\n * Creates invalid database number error.\n */\n static invalidDb(db: number, max: number = 15): RedisConfigError {\n return new RedisConfigError(`Invalid database number ${db}. Must be between 0 and ${max}`, ErrorCode.CFG_INVALID_DB, undefined, { db, max });\n }\n\n /**\n * Creates invalid TTL error.\n */\n static invalidTTL(ttl: number, reason?: string): RedisConfigError {\n const message = reason ? `Invalid TTL ${ttl}: ${reason}` : `Invalid TTL ${ttl}. Must be a positive number`;\n\n return new RedisConfigError(message, ErrorCode.CFG_INVALID_TTL, undefined, { ttl, reason });\n }\n\n /**\n * Creates invalid timeout error.\n */\n static invalidTimeout(timeout: number, field: string, reason?: string): RedisConfigError {\n const message = reason ? `Invalid ${field} timeout ${timeout}: ${reason}` : `Invalid ${field} timeout ${timeout}. Must be a positive number`;\n\n return new RedisConfigError(message, ErrorCode.CFG_INVALID_TIMEOUT, undefined, { timeout, field, reason });\n }\n\n /**\n * Creates invalid retry configuration error.\n */\n static invalidRetry(reason: string, context?: Record<string, unknown>): RedisConfigError {\n return new RedisConfigError(`Invalid retry configuration: ${reason}`, ErrorCode.CFG_INVALID_RETRY, undefined, context);\n }\n\n /**\n * Creates incompatible configuration error.\n */\n static incompatible(option1: string, option2: string, reason?: string): RedisConfigError {\n const message = reason ? `Incompatible configuration: ${option1} and ${option2} - ${reason}` : `Configuration options \"${option1}\" and \"${option2}\" are incompatible`;\n\n return new RedisConfigError(message, ErrorCode.CFG_INCOMPATIBLE, undefined, { option1, option2, reason });\n }\n\n /**\n * Creates driver not supported error.\n */\n static driverNotSupported(driver: string, supportedDrivers: string[]): RedisConfigError {\n return new RedisConfigError(`Driver \"${driver}\" is not supported. Available drivers: ${supportedDrivers.join(', ')}`, ErrorCode.CFG_DRIVER_NOT_SUPPORTED, undefined, { driver, supportedDrivers });\n }\n\n /**\n * Creates invalid cluster nodes error.\n */\n static invalidClusterNodes(reason: string, nodes?: Array<{ host: string; port: number }>): RedisConfigError {\n return new RedisConfigError(`Invalid cluster nodes configuration: ${reason}`, ErrorCode.CFG_INVALID_CLUSTER_NODES, undefined, { reason, nodes, nodeCount: nodes?.length });\n }\n\n /**\n * Creates invalid sentinel configuration error.\n */\n static invalidSentinel(reason: string, context?: Record<string, unknown>): RedisConfigError {\n return new RedisConfigError(`Invalid sentinel configuration: ${reason}`, ErrorCode.CFG_INVALID_SENTINEL, undefined, context);\n }\n\n /**\n * Creates invalid TLS configuration error.\n */\n static invalidTLS(reason: string, context?: Record<string, unknown>): RedisConfigError {\n return new RedisConfigError(`Invalid TLS configuration: ${reason}`, ErrorCode.CFG_INVALID_TLS, undefined, context);\n }\n}\n\n/**\n * Error thrown when configuration validation fails.\n *\n * Used for complex validation rules and schema validation.\n *\n * @example\n * ```typescript\n * throw new RedisValidationError(\n * 'clients',\n * [\n * { field: 'host', message: 'Host is required' },\n * { field: 'port', message: 'Port must be between 1 and 65535' },\n * ],\n * );\n * ```\n */\nexport class RedisValidationError extends RedisConfigError {\n constructor(\n public readonly field: string,\n public readonly errors: Array<{\n field: string;\n message: string;\n value?: unknown;\n }>,\n cause?: Error,\n ) {\n const errorMessages = errors.map((e) => `${e.field}: ${e.message}`).join('; ');\n\n super(`Validation failed for \"${field}\": ${errorMessages}`, ErrorCode.CFG_VALIDATION_FAILED, cause, { field, errors, errorCount: errors.length });\n }\n\n /**\n * Creates validation error from single field.\n */\n static single(field: string, message: string, value?: unknown): RedisValidationError {\n return new RedisValidationError(field, [{ field, message, value }]);\n }\n\n /**\n * Creates validation error from multiple fields.\n */\n static multiple(parent: string, errors: Array<{ field: string; message: string; value?: unknown }>): RedisValidationError {\n return new RedisValidationError(parent, errors);\n }\n\n /**\n * Adds a validation error to the list.\n */\n addError(field: string, message: string, value?: unknown): this {\n this.errors.push({ field, message, value });\n return this;\n }\n\n /**\n * Checks if validation has errors.\n */\n hasErrors(): boolean {\n return this.errors.length > 0;\n }\n\n /**\n * Gets all error messages.\n */\n getMessages(): string[] {\n return this.errors.map((e) => `${e.field}: ${e.message}`);\n }\n\n /**\n * Gets errors for specific field.\n */\n getFieldErrors(field: string): Array<{ field: string; message: string; value?: unknown }> {\n return this.errors.filter((e) => e.field === field);\n }\n}\n\n/**\n * Validation error collector for building up multiple validation errors.\n *\n * @example\n * ```typescript\n * const validator = new ValidationErrorCollector('connectionConfig');\n *\n * if (!config.host) {\n * validator.add('host', 'Host is required');\n * }\n *\n * if (config.port < 1 || config.port > 65535) {\n * validator.add('port', 'Port must be between 1 and 65535', config.port);\n * }\n *\n * validator.throwIfErrors();\n * ```\n */\nexport class ValidationErrorCollector {\n private errors: Array<{\n field: string;\n message: string;\n value?: unknown;\n }> = [];\n\n constructor(private readonly parent: string) {}\n\n /**\n * Adds a validation error.\n */\n add(field: string, message: string, value?: unknown): this {\n this.errors.push({ field, message, value });\n return this;\n }\n\n /**\n * Checks if there are any errors.\n */\n hasErrors(): boolean {\n return this.errors.length > 0;\n }\n\n /**\n * Gets error count.\n */\n count(): number {\n return this.errors.length;\n }\n\n /**\n * Throws if there are any errors.\n */\n throwIfErrors(): void {\n if (this.hasErrors()) {\n throw new RedisValidationError(this.parent, this.errors);\n }\n }\n\n /**\n * Gets all errors.\n */\n getErrors(): Array<{ field: string; message: string; value?: unknown }> {\n return [...this.errors];\n }\n\n /**\n * Clears all errors.\n */\n clear(): void {\n this.errors = [];\n }\n}\n","/**\n * Driver-specific error classes with simplified constructors.\n * Used internally by driver adapters (ioredis, node-redis).\n */\n\nimport { RedisConnectionError, RedisTimeoutError as RedisTimeoutErrorNew, RedisOperationError } from '../../errors';\nimport { ErrorCode } from '../../errors/error-codes';\n\n/**\n * Base class for driver-related errors.\n */\nexport class DriverError extends RedisConnectionError {\n constructor(message: string, code: ErrorCode, cause?: Error, context?: Record<string, unknown>) {\n super(message, code, undefined, undefined, cause, context);\n }\n}\n\n/**\n * Error thrown when connection to Redis fails.\n */\nexport class ConnectionError extends RedisConnectionError {\n constructor(\n message: string,\n cause?: Error,\n public override readonly host?: string,\n public override readonly port?: number,\n ) {\n super(message, ErrorCode.CONN_FAILED, host, port, cause);\n }\n}\n\n/**\n * Error thrown when operation times out.\n */\nexport class TimeoutError extends RedisTimeoutErrorNew {\n constructor(operation: string, timeoutMs: number, cause?: Error) {\n super(operation, timeoutMs, undefined, undefined, cause);\n }\n}\n\n/**\n * Error thrown when Redis command fails.\n */\nexport class CommandError extends RedisOperationError {\n public override readonly command: string;\n public readonly args: unknown[];\n\n constructor(command: string, args: unknown[], cause?: Error) {\n const message = `Command \"${command}\" failed: ${cause?.message ?? 'Unknown error'}`;\n super(message, ErrorCode.OP_FAILED, command, cause, { argsCount: args.length });\n\n this.command = command;\n this.args = args;\n }\n}\n","import Redis, { Cluster, RedisOptions, ClusterOptions, ClusterNode } from 'ioredis';\n\nimport { BaseRedisDriver } from './base.driver';\nimport { IPipeline, IMulti, DriverEvent } from '../../interfaces';\nimport { ConnectionError, CommandError } from '../../shared/errors';\nimport { ConnectionConfig, isSingleConnection, isClusterConnection, isSentinelConnection } from '../../types';\n\n/**\n * IoRedis driver adapter.\n *\n * Implements IRedisDriver using ioredis library.\n * Supports single instance, cluster, and sentinel configurations.\n *\n * @example\n * ```typescript\n * const driver = new IoRedisAdapter({\n * host: 'localhost',\n * port: 6379,\n * });\n * await driver.connect();\n * ```\n */\nexport class IoRedisAdapter extends BaseRedisDriver {\n private client: Redis | Cluster | null = null;\n\n constructor(\n config: ConnectionConfig,\n options?: {\n enableLogging?: boolean;\n },\n ) {\n super(config, options);\n }\n\n protected async doConnect(): Promise<void> {\n try {\n // Create client based on configuration type\n if (isSingleConnection(this.config)) {\n this.client = this.createSingleClient(this.config);\n } else if (isClusterConnection(this.config)) {\n this.client = this.createClusterClient(this.config);\n } else if (isSentinelConnection(this.config)) {\n this.client = this.createSentinelClient(this.config);\n } else {\n throw new ConnectionError('Unknown connection type');\n }\n\n // Setup event handlers\n this.setupEventHandlers();\n\n // Connect and wait for ready\n await new Promise<void>((resolve, reject) => {\n const onReady = (): void => {\n cleanup();\n resolve();\n };\n const onError = (error: Error): void => {\n cleanup();\n reject(new ConnectionError('Connection failed', error));\n };\n const cleanup = (): void => {\n this.client?.off('ready', onReady);\n this.client?.off('error', onError);\n };\n\n this.client?.once('ready', onReady);\n this.client?.once('error', onError);\n\n // Initiate connection (required when lazyConnect is true)\n if (this.client) {\n if (this.client instanceof Redis) {\n this.client.connect().catch(reject);\n } else if (this.client instanceof Cluster) {\n this.client.connect().catch(reject);\n }\n }\n });\n } catch (error) {\n if (this.client) {\n this.client.disconnect();\n this.client = null;\n }\n throw error instanceof ConnectionError ? error : new ConnectionError('Failed to connect', error as Error);\n }\n }\n\n protected async doDisconnect(): Promise<void> {\n if (!this.client) {\n return;\n }\n\n try {\n await this.client.quit();\n } catch {\n // Force disconnect on error\n this.client.disconnect();\n } finally {\n this.client = null;\n }\n }\n\n protected async executeCommand(command: string, ...args: unknown[]): Promise<unknown> {\n if (!this.client) {\n throw new ConnectionError('Client not initialized');\n }\n\n try {\n // IoRedis uses lowercase command names\n const method = command.toLowerCase();\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fn = (this.client as any)[method];\n\n if (typeof fn !== 'function') {\n throw new CommandError(command, args);\n }\n\n return await fn.apply(this.client, args);\n } catch (error) {\n throw new CommandError(command, args, error as Error);\n }\n }\n\n protected createPipeline(): IPipeline {\n if (!this.client) {\n throw new ConnectionError('Client not initialized');\n }\n\n const ioPipeline = this.client.pipeline();\n\n return new IoRedisPipelineAdapter(ioPipeline);\n }\n\n protected createMulti(): IMulti {\n if (!this.client) {\n throw new ConnectionError('Client not initialized');\n }\n\n const ioMulti = this.client.multi();\n\n return new IoRedisMultiAdapter(ioMulti);\n }\n\n private createSingleClient(config: ConnectionConfig): Redis {\n // Use type guard to ensure we have ISingleConnectionConfig\n if (!isSingleConnection(config)) {\n throw new ConnectionError('Invalid single connection configuration');\n }\n\n const options: RedisOptions = {\n host: config.host ?? 'localhost',\n port: config.port ?? 6379,\n password: config.password,\n db: config.db ?? 0,\n keyPrefix: config.keyPrefix,\n connectTimeout: config.connectTimeout ?? 10000,\n commandTimeout: config.commandTimeout ?? 5000,\n keepAlive: config.keepAlive ?? 0,\n enableReadyCheck: config.enableAutoReconnect ?? true,\n maxRetriesPerRequest: config.maxRetriesPerRequest ?? 3,\n retryStrategy: config.retryStrategy,\n reconnectOnError: config.reconnectOnError,\n lazyConnect: true,\n };\n\n // TLS configuration\n if (config.tls?.enabled) {\n options.tls = {\n ca: config.tls.ca,\n cert: config.tls.cert,\n key: config.tls.key,\n rejectUnauthorized: config.tls.rejectUnauthorized ?? true,\n };\n }\n\n return new Redis(options);\n }\n\n private createClusterClient(config: ConnectionConfig): Cluster {\n if (!isClusterConnection(config)) {\n throw new ConnectionError('Invalid cluster configuration');\n }\n\n const nodes: ClusterNode[] = config.nodes.map((node) => ({\n host: node.host,\n port: node.port,\n }));\n\n const options: ClusterOptions = {\n redisOptions: {\n password: config.password,\n db: config.db ?? 0,\n keyPrefix: config.keyPrefix,\n connectTimeout: config.connectTimeout ?? 10000,\n commandTimeout: config.commandTimeout ?? 5000,\n maxRetriesPerRequest: config.maxRetriesPerRequest ?? 3,\n },\n clusterRetryStrategy: config.retryStrategy,\n enableReadyCheck: config.clusterOptions?.enableReadyCheck ?? false,\n maxRedirections: config.clusterOptions?.maxRedirections ?? 16,\n retryDelayOnClusterDown: config.clusterOptions?.retryDelayOnClusterDown ?? 100,\n retryDelayOnFailover: config.clusterOptions?.retryDelayOnFailover ?? 100,\n scaleReads: config.clusterOptions?.scaleReads ?? 'master',\n lazyConnect: true,\n // Pass through any additional cluster options (e.g., natMap)\n ...config.clusterOptions,\n };\n\n return new Cluster(nodes, options);\n }\n\n private createSentinelClient(config: ConnectionConfig): Redis {\n if (!isSentinelConnection(config)) {\n throw new ConnectionError('Invalid sentinel configuration');\n }\n\n const options: RedisOptions = {\n sentinels: config.sentinels,\n name: config.name,\n password: config.password,\n db: config.db ?? 0,\n keyPrefix: config.keyPrefix,\n connectTimeout: config.connectTimeout ?? 10000,\n commandTimeout: config.commandTimeout ?? 5000,\n maxRetriesPerRequest: config.maxRetriesPerRequest ?? 3,\n retryStrategy: config.retryStrategy,\n sentinelRetryStrategy: config.sentinelOptions?.sentinelRetryStrategy,\n sentinelPassword: config.sentinelOptions?.sentinelPassword,\n enableTLSForSentinelMode: config.sentinelOptions?.enableTLSForSentinelMode,\n lazyConnect: true,\n // NAT mapping for Docker/firewall scenarios\n // Maps internal Redis addresses to external accessible addresses\n natMap: config.sentinelOptions?.natMap,\n };\n\n return new Redis(options);\n }\n\n private setupEventHandlers(): void {\n if (!this.client) {\n return;\n }\n\n this.client.on('connect', () => {\n this.emit(DriverEvent.CONNECT);\n });\n\n this.client.on('ready', () => {\n this.emit(DriverEvent.READY);\n });\n\n this.client.on('error', (error: Error) => {\n this.emit(DriverEvent.ERROR, error);\n });\n\n this.client.on('close', () => {\n this.emit(DriverEvent.CLOSE);\n });\n\n this.client.on('reconnecting', () => {\n this.emit(DriverEvent.RECONNECTING);\n });\n\n this.client.on('end', () => {\n this.emit(DriverEvent.END);\n });\n }\n\n /**\n * Gets underlying ioredis client.\n * For advanced usage only.\n */\n getClient(): Redis | Cluster | null {\n return this.client;\n }\n}\n\n/**\n * IoRedis pipeline adapter.\n */\nclass IoRedisPipelineAdapter implements IPipeline {\n constructor(private readonly pipeline: ReturnType<Redis['pipeline']>) {}\n\n async exec(): Promise<Array<[Error | null, unknown]>> {\n const results = await this.pipeline.exec();\n if (!results) {\n return [];\n }\n return results.map(([error, result]) => [error, result]);\n }\n\n get(key: string): this {\n this.pipeline.get(key);\n return this;\n }\n\n set(key: string, value: string, options?: { ex?: number; px?: number }): this {\n if (options?.ex) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (this.pipeline.set as any)(key, value, 'EX', options.ex);\n } else if (options?.px) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (this.pipeline.set as any)(key, value, 'PX', options.px);\n } else {\n this.pipeline.set(key, value);\n }\n return this;\n }\n\n del(...keys: string[]): this {\n this.pipeline.del(...keys);\n return this;\n }\n\n mget(...keys: string[]): this {\n this.pipeline.mget(...keys);\n return this;\n }\n\n mset(data: Record<string, string>): this {\n this.pipeline.mset(data);\n return this;\n }\n\n expire(key: string, seconds: number): this {\n this.pipeline.expire(key, seconds);\n return this;\n }\n\n ttl(key: string): this {\n this.pipeline.ttl(key);\n return this;\n }\n\n incr(key: string): this {\n this.pipeline.incr(key);\n return this;\n }\n\n incrby(key: string, increment: number): this {\n this.pipeline.incrby(key, increment);\n return this;\n }\n\n hget(key: string, field: string): this {\n this.pipeline.hget(key, field);\n return this;\n }\n\n hset(key: string, field: string, value: string): this {\n this.pipeline.hset(key, field, value);\n return this;\n }\n\n hmset(key: string, data: Record<string, string>): this {\n this.pipeline.hmset(key, data);\n return this;\n }\n\n hgetall(key: string): this {\n this.pipeline.hgetall(key);\n return this;\n }\n\n lpush(key: string, ...values: string[]): this {\n this.pipeline.lpush(key, ...values);\n return this;\n }\n\n rpush(key: string, ...values: string[]): this {\n this.pipeline.rpush(key, ...values);\n return this;\n }\n\n sadd(key: string, ...members: string[]): this {\n this.pipeline.sadd(key, ...members);\n return this;\n }\n\n srem(key: string, ...members: string[]): this {\n this.pipeline.srem(key, ...members);\n return this;\n }\n\n zadd(key: string, ...args: Array<number | string>): this {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (this.pipeline.zadd as any)(key, ...args);\n return this;\n }\n\n zrem(key: string, ...members: string[]): this {\n this.pipeline.zrem(key, ...members);\n return this;\n }\n}\n\n/**\n * IoRedis multi/exec adapter.\n */\nclass IoRedisMultiAdapter extends IoRedisPipelineAdapter implements IMulti {\n constructor(private readonly multi: ReturnType<Redis['multi']>) {\n super(multi);\n }\n\n discard(): void {\n this.multi.discard();\n }\n}\n","/**\n * Configuration types for NestJS RedisX.\n * Based on ARCHITECTURE.md - Configuration Schema.\n */\n\nimport { ModuleMetadata, Type } from '@nestjs/common';\n\nimport { IRedisXPlugin } from '../plugin/domain/interfaces';\n\n/**\n * Base connection options common to all connection types.\n */\nexport interface IBaseConnectionConfig {\n /**\n * Password for authentication.\n */\n password?: string;\n\n /**\n * Database number (0-15).\n * @default 0\n */\n db?: number;\n\n /**\n * Key prefix for all operations.\n * @example 'myapp:'\n */\n keyPrefix?: string;\n\n /**\n * Connection timeout in milliseconds.\n * @default 10000\n */\n connectTimeout?: number;\n\n /**\n * Command timeout in milliseconds.\n * @default 5000\n */\n commandTimeout?: number;\n\n /**\n * Keep-alive interval in milliseconds.\n * @default 0 (disabled)\n */\n keepAlive?: number;\n\n /**\n * Enable offline queue.\n * @default true\n */\n enableOfflineQueue?: boolean;\n\n /**\n * Enable auto-reconnect.\n * @default true\n */\n enableAutoReconnect?: boolean;\n\n /**\n * Maximum retry attempts for commands.\n * @default 3\n */\n maxRetriesPerRequest?: number;\n\n /**\n * Retry strategy function.\n */\n retryStrategy?: (times: number) => number | null;\n\n /**\n * Reconnect on error function.\n */\n reconnectOnError?: (error: Error) => boolean | 1 | 2;\n}\n\n/**\n * Single instance Redis connection configuration.\n */\nexport interface ISingleConnectionConfig extends IBaseConnectionConfig {\n /**\n * Connection type discriminator.\n */\n type?: 'single';\n\n /**\n * Redis host.\n * @default 'localhost'\n */\n host?: string;\n\n /**\n * Redis port.\n * @default 6379\n */\n port?: number;\n\n /**\n * Optional TLS configuration.\n */\n tls?: ITlsConfig;\n}\n\n/**\n * Redis Cluster connection configuration.\n */\nexport interface IClusterConnectionConfig extends IBaseConnectionConfig {\n /**\n * Connection type discriminator.\n */\n type: 'cluster';\n\n /**\n * Cluster nodes.\n * @example [{ host: 'localhost', port: 6379 }, { host: 'localhost', port: 6380 }]\n */\n nodes: Array<{\n host: string;\n port: number;\n }>;\n\n /**\n * Cluster-specific options.\n */\n clusterOptions?: {\n /**\n * Maximum number of redirections.\n * @default 16\n */\n maxRedirections?: number;\n\n /**\n * Retry delay for cluster commands.\n * @default 100\n */\n retryDelayOnClusterDown?: number;\n\n /**\n * Retry delay when all slots covered.\n * @default 100\n */\n retryDelayOnFailover?: number;\n\n /**\n * Scale reads to slaves.\n * @default 'master'\n */\n scaleReads?: 'master' | 'slave' | 'all';\n\n /**\n * Enable readonly mode for slave nodes.\n * @default false\n */\n enableReadyCheck?: boolean;\n\n /**\n * NAT mapping for Docker/firewall scenarios.\n * Maps internal IPs to external accessible addresses.\n * @example { '172.17.0.2:6379': { host: 'localhost', port: 6379 } }\n */\n natMap?: Record<string, { host: string; port: number }>;\n\n /**\n * Additional ioredis cluster options.\n * Allows passing any other ioredis ClusterOptions.\n */\n [key: string]: unknown;\n };\n}\n\n/**\n * Redis Sentinel connection configuration.\n */\nexport interface ISentinelConnectionConfig extends IBaseConnectionConfig {\n /**\n * Connection type discriminator.\n */\n type: 'sentinel';\n\n /**\n * Sentinel nodes.\n */\n sentinels: Array<{\n host: string;\n port: number;\n }>;\n\n /**\n * Master name.\n */\n name: string;\n\n /**\n * Optional TLS configuration.\n */\n tls?: ITlsConfig;\n\n /**\n * Sentinel-specific options.\n */\n sentinelOptions?: {\n /**\n * Enable TLS for sentinel connections.\n */\n enableTLSForSentinelMode?: boolean;\n\n /**\n * Sentinel password (if different from node password).\n */\n sentinelPassword?: string;\n\n /**\n * Retry strategy for sentinel.\n */\n sentinelRetryStrategy?: (times: number) => number | null;\n\n /**\n * Prefer slave nodes for reads.\n * @default false\n */\n preferredSlaves?: boolean;\n\n /**\n * NAT mapping for sentinel-managed instances.\n * Maps internal master/slave addresses to external accessible addresses.\n * @example { 'redis-master:6379': { host: 'localhost', port: 6379 } }\n */\n natMap?: Record<string, { host: string; port: number }>;\n\n /**\n * Master connection pool size.\n * Number of connections to maintain to the master node.\n * @default 1\n */\n masterPoolSize?: number;\n\n /**\n * Replica connection pool size per node.\n * Set to 0 to disable replica connections.\n * @default 0\n */\n replicaPoolSize?: number;\n\n /**\n * Interval in milliseconds to scan for topology changes.\n * @default 10000\n */\n scanInterval?: number;\n\n /**\n * Maximum number of command rediscovers on topology change.\n * @default 16\n */\n maxCommandRediscovers?: number;\n\n /**\n * Additional ioredis/node-redis sentinel options.\n */\n [key: string]: unknown;\n };\n}\n\n/**\n * TLS configuration.\n */\nexport interface ITlsConfig {\n /**\n * Enable TLS.\n * @default false\n */\n enabled?: boolean;\n\n /**\n * CA certificate.\n */\n ca?: string | Buffer;\n\n /**\n * Client certificate.\n */\n cert?: string | Buffer;\n\n /**\n * Client key.\n */\n key?: string | Buffer;\n\n /**\n * Reject unauthorized certificates.\n * @default true\n */\n rejectUnauthorized?: boolean;\n}\n\n/**\n * Union type for all connection configurations.\n */\nexport type ConnectionConfig = ISingleConnectionConfig | IClusterConnectionConfig | ISentinelConnectionConfig;\n\n/**\n * Configuration for a single Redis client.\n */\nexport interface IRedisClientOptions {\n /**\n * Client name (for named clients).\n * @default 'default'\n */\n name?: string;\n\n /**\n * Connection configuration.\n */\n connection: ConnectionConfig;\n\n /**\n * Namespace/prefix for this client.\n * Overrides connection.keyPrefix.\n */\n namespace?: string;\n}\n\n/**\n * Driver type for Redis connections.\n */\nexport type DriverType = 'ioredis' | 'node-redis';\n\n/**\n * Global module configuration options.\n */\nexport interface IGlobalConfig {\n /**\n * Redis driver to use.\n * @default 'ioredis'\n */\n driver?: DriverType;\n\n /**\n * Enable debug logging.\n * @default false\n */\n debug?: boolean;\n\n /**\n * Default TTL for cache operations (seconds).\n * @default 3600\n */\n defaultTtl?: number;\n\n /**\n * Global key prefix for all clients.\n */\n keyPrefix?: string;\n\n /**\n * Enable graceful shutdown.\n * @default true\n */\n gracefulShutdown?: boolean;\n\n /**\n * Graceful shutdown timeout (milliseconds).\n * @default 5000\n */\n gracefulShutdownTimeout?: number;\n\n /**\n * Enable health checks.\n * @default true\n */\n enableHealthChecks?: boolean;\n\n /**\n * Health check interval (milliseconds).\n * @default 30000\n */\n healthCheckInterval?: number;\n}\n\n/**\n * Synchronous module configuration.\n */\nexport interface IRedisModuleOptions {\n /**\n * Redis client configurations.\n * Can be a single config or a map of named clients.\n */\n clients: ConnectionConfig | Record<string, ConnectionConfig>;\n\n /**\n * Registered plugins.\n */\n plugins?: IRedisXPlugin[];\n\n /**\n * Global configuration.\n */\n global?: IGlobalConfig;\n}\n\n/**\n * Factory function for async configuration.\n */\nexport interface IRedisModuleOptionsFactory {\n /**\n * Creates module options asynchronously.\n */\n createRedisModuleOptions(): Promise<IRedisModuleOptions> | IRedisModuleOptions;\n}\n\n/**\n * Asynchronous module configuration.\n */\nexport interface IRedisModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {\n /**\n * Registered plugins.\n * Plugins must be provided outside useFactory to ensure providers are available at module construction time.\n * This is a standard NestJS pattern (similar to @nestjs/typeorm entities, @nestjs/graphql resolvers).\n */\n plugins?: IRedisXPlugin[];\n\n /**\n * Factory function returning options.\n */\n useFactory?: (...args: unknown[]) => Promise<IRedisModuleOptions> | IRedisModuleOptions;\n\n /**\n * Dependencies to inject into factory.\n */\n inject?: unknown[];\n\n /**\n * Use existing provider.\n */\n useExisting?: Type<IRedisModuleOptionsFactory>;\n\n /**\n * Use class provider.\n */\n useClass?: Type<IRedisModuleOptionsFactory>;\n}\n\n/**\n * Client metadata stored in the registry.\n */\nexport interface IClientMetadata {\n /**\n * Client name.\n */\n name: string;\n\n /**\n * Connection configuration.\n */\n config: ConnectionConfig;\n\n /**\n * Connection status.\n */\n status: ConnectionStatus;\n\n /**\n * Last connection time.\n */\n connectedAt?: Date;\n\n /**\n * Last error.\n */\n lastError?: Error;\n\n /**\n * Number of reconnection attempts.\n */\n reconnectAttempts: number;\n}\n\n/**\n * Connection status.\n */\nexport enum ConnectionStatus {\n DISCONNECTED = 'disconnected',\n CONNECTING = 'connecting',\n CONNECTED = 'connected',\n RECONNECTING = 'reconnecting',\n ERROR = 'error',\n}\n\n/**\n * Health check result.\n */\nexport interface IHealthCheckResult {\n /**\n * Overall health status.\n */\n status: 'healthy' | 'unhealthy' | 'degraded';\n\n /**\n * Individual client statuses.\n */\n clients: Record<string, IClientHealthStatus>;\n\n /**\n * Timestamp of check.\n */\n timestamp: Date;\n}\n\n/**\n * Client health status.\n */\nexport interface IClientHealthStatus {\n /**\n * Client name.\n */\n name: string;\n\n /**\n * Connection status.\n */\n status: ConnectionStatus;\n\n /**\n * Latency in milliseconds (ping).\n */\n latency?: number;\n\n /**\n * Last error message.\n */\n error?: string;\n\n /**\n * Uptime in milliseconds.\n */\n uptime?: number;\n}\n\n/**\n * Type guard for single connection config.\n */\nexport function isSingleConnection(config: ConnectionConfig): config is ISingleConnectionConfig {\n return !('type' in config) || config.type === 'single';\n}\n\n/**\n * Type guard for cluster connection config.\n */\nexport function isClusterConnection(config: ConnectionConfig): config is IClusterConnectionConfig {\n return 'type' in config && config.type === 'cluster';\n}\n\n/**\n * Type guard for sentinel connection config.\n */\nexport function isSentinelConnection(config: ConnectionConfig): config is ISentinelConnectionConfig {\n return 'type' in config && config.type === 'sentinel';\n}\n\n/**\n * Type guard for async options with factory.\n */\nexport function hasFactory(options: IRedisModuleAsyncOptions): options is Required<Pick<IRedisModuleAsyncOptions, 'useFactory'>> & IRedisModuleAsyncOptions {\n return options.useFactory !== undefined;\n}\n\n/**\n * Type guard for async options with existing provider.\n */\nexport function hasExisting(options: IRedisModuleAsyncOptions): options is Required<Pick<IRedisModuleAsyncOptions, 'useExisting'>> & IRedisModuleAsyncOptions {\n return options.useExisting !== undefined;\n}\n\n/**\n * Type guard for async options with class provider.\n */\nexport function hasClass(options: IRedisModuleAsyncOptions): options is Required<Pick<IRedisModuleAsyncOptions, 'useClass'>> & IRedisModuleAsyncOptions {\n return options.useClass !== undefined;\n}\n","import { createClient, createCluster, createSentinel, RedisClientOptions, RedisClientType, RedisClusterType, RedisSentinelType } from 'redis';\n\nimport { BaseRedisDriver } from './base.driver';\nimport { IPipeline, IMulti, DriverEvent } from '../../interfaces';\nimport { ConnectionError, CommandError } from '../../shared/errors';\nimport { ConnectionConfig, isSingleConnection, isClusterConnection, isSentinelConnection } from '../../types';\n\n/** Base delay for exponential backoff in reconnection strategy. */\nconst RETRY_BASE_DELAY_MS = 100;\n\n/** Maximum delay cap for reconnection backoff. */\nconst RETRY_MAX_DELAY_MS = 30_000;\n\n/**\n * Union type for all supported node-redis client types.\n */\ntype NodeRedisClient = RedisClientType | RedisClusterType | RedisSentinelType;\n\n/**\n * Node-Redis driver adapter.\n *\n * Implements IRedisDriver using redis (node-redis v4+) library.\n * Supports single instance, cluster, and sentinel configurations.\n *\n * @example\n * ```typescript\n * // Single instance\n * const driver = new NodeRedisAdapter({\n * type: 'single',\n * host: 'localhost',\n * port: 6379,\n * });\n *\n * // Cluster\n * const clusterDriver = new NodeRedisAdapter({\n * type: 'cluster',\n * nodes: [{ host: 'node1', port: 7000 }],\n * });\n *\n * // Sentinel\n * const sentinelDriver = new NodeRedisAdapter({\n * type: 'sentinel',\n * sentinels: [{ host: 'sentinel1', port: 26379 }],\n * name: 'mymaster',\n * });\n *\n * await driver.connect();\n * ```\n */\nexport class NodeRedisAdapter extends BaseRedisDriver {\n private client: NodeRedisClient | null = null;\n private isSentinel = false;\n private isCluster = false;\n private readonly keyPrefix: string;\n\n constructor(\n config: ConnectionConfig,\n options?: {\n enableLogging?: boolean;\n },\n ) {\n super(config, options);\n this.keyPrefix = config.keyPrefix ?? '';\n }\n\n protected async doConnect(): Promise<void> {\n try {\n // Create client based on configuration type\n if (isSingleConnection(this.config)) {\n this.client = this.createSingleClient(this.config);\n this.isSentinel = false;\n } else if (isClusterConnection(this.config)) {\n this.client = this.createClusterClient(this.config);\n this.isSentinel = false;\n this.isCluster = true;\n } else if (isSentinelConnection(this.config)) {\n this.client = await this.createSentinelClient(this.config);\n this.isSentinel = true;\n } else {\n throw new ConnectionError('Unknown connection type');\n }\n\n // Setup event handlers\n this.setupEventHandlers();\n\n // Connect (sentinel already connected during creation)\n if (!this.isSentinel) {\n await this.client.connect();\n }\n } catch (error) {\n if (this.client) {\n await this.safeDisconnect();\n this.client = null;\n }\n throw error instanceof ConnectionError ? error : new ConnectionError('Failed to connect', error as Error);\n }\n }\n\n protected async doDisconnect(): Promise<void> {\n if (!this.client) {\n return;\n }\n\n try {\n if (this.isSentinel) {\n // Sentinel uses close() instead of quit()\n await (this.client as RedisSentinelType).close();\n } else {\n // Single and cluster clients have quit()\n await (this.client as RedisClientType | RedisClusterType).quit();\n }\n } catch {\n // Force disconnect on error\n await this.safeDisconnect();\n } finally {\n this.client = null;\n this.isSentinel = false;\n this.isCluster = false;\n }\n }\n\n protected async executeCommand(command: string, ...args: unknown[]): Promise<unknown> {\n if (!this.client) {\n throw new ConnectionError('Client not initialized');\n }\n\n try {\n // Convert arguments properly (not just toString everything)\n const commandArgs = this.convertArgs(args);\n\n // Apply key prefix to first argument if it's a key-based command\n const prefixedArgs = this.applyKeyPrefix(command, commandArgs);\n\n if (this.isSentinel) {\n // Sentinel uses .use() to get a client from the pool\n return await (this.client as RedisSentinelType).use(async (client) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return await (client as any).sendCommand([command, ...prefixedArgs]);\n });\n }\n\n if (this.isCluster) {\n // Cluster uses different sendCommand signature:\n // sendCommand(firstKey, isReadonly, args, options?)\n const firstKey = this.getFirstKey(command, prefixedArgs);\n const isReadonly = this.isReadonlyCommand(command);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return await (this.client as any).sendCommand(firstKey, isReadonly, [command, ...prefixedArgs]);\n }\n\n // Execute command directly for regular clients\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return await (this.client as any).sendCommand([command, ...prefixedArgs]);\n } catch (error) {\n throw new CommandError(command, args, error as Error);\n }\n }\n\n protected createPipeline(): IPipeline {\n if (!this.client) {\n throw new ConnectionError('Client not initialized');\n }\n\n if (this.isSentinel) {\n // For sentinel, we need to use the client from the pool\n return new NodeRedisSentinelPipelineAdapter(this.client as RedisSentinelType, this.keyPrefix);\n }\n\n return new NodeRedisPipelineAdapter(this.client as RedisClientType | RedisClusterType, this.keyPrefix);\n }\n\n protected createMulti(): IMulti {\n if (!this.client) {\n throw new ConnectionError('Client not initialized');\n }\n\n if (this.isSentinel) {\n return new NodeRedisSentinelMultiAdapter(this.client as RedisSentinelType, this.keyPrefix);\n }\n\n return new NodeRedisMultiAdapter(this.client as RedisClientType | RedisClusterType, this.keyPrefix);\n }\n\n /**\n * Override hgetall to convert array response to object.\n * Node-redis sendCommand returns ['field1', 'value1', 'field2', 'value2']\n * but we need { field1: 'value1', field2: 'value2' }\n */\n override async hgetall(key: string): Promise<Record<string, string>> {\n this.assertConnected();\n const result = await this.executeCommand('HGETALL', key);\n\n // Convert array to object\n if (Array.isArray(result)) {\n const obj: Record<string, string> = {};\n for (let i = 0; i < result.length; i += 2) {\n obj[result[i]] = result[i + 1];\n }\n return obj;\n }\n\n return (result as Record<string, string>) ?? {};\n }\n\n /**\n * Override scan to normalize cursor response.\n * Node-redis sendCommand returns [cursor, [keys...]]\n */\n override async scan(cursor: number, options?: { match?: string; count?: number }): Promise<[string, string[]]> {\n this.assertConnected();\n const args: unknown[] = [cursor];\n\n if (options?.match) {\n args.push('MATCH', options.match);\n }\n if (options?.count) {\n args.push('COUNT', options.count);\n }\n\n const result = await this.executeCommand('SCAN', ...args);\n\n if (Array.isArray(result) && result.length === 2) {\n return [String(result[0]), result[1] as string[]];\n }\n\n return ['0', []];\n }\n\n /**\n * Override hscan to normalize cursor response.\n */\n override async hscan(key: string, cursor: number, options?: { match?: string; count?: number }): Promise<[string, string[]]> {\n this.assertConnected();\n const args: unknown[] = [key, cursor];\n\n if (options?.match) {\n args.push('MATCH', options.match);\n }\n if (options?.count) {\n args.push('COUNT', options.count);\n }\n\n const result = await this.executeCommand('HSCAN', ...args);\n\n if (Array.isArray(result) && result.length === 2) {\n return [String(result[0]), result[1] as string[]];\n }\n\n return ['0', []];\n }\n\n /**\n * Override sscan to normalize cursor response.\n */\n override async sscan(key: string, cursor: number, options?: { match?: string; count?: number }): Promise<[string, string[]]> {\n this.assertConnected();\n const args: unknown[] = [key, cursor];\n\n if (options?.match) {\n args.push('MATCH', options.match);\n }\n if (options?.count) {\n args.push('COUNT', options.count);\n }\n\n const result = await this.executeCommand('SSCAN', ...args);\n\n if (Array.isArray(result) && result.length === 2) {\n return [String(result[0]), result[1] as string[]];\n }\n\n return ['0', []];\n }\n\n /**\n * Override zscan to normalize cursor response.\n */\n override async zscan(key: string, cursor: number, options?: { match?: string; count?: number }): Promise<[string, string[]]> {\n this.assertConnected();\n const args: unknown[] = [key, cursor];\n\n if (options?.match) {\n args.push('MATCH', options.match);\n }\n if (options?.count) {\n args.push('COUNT', options.count);\n }\n\n const result = await this.executeCommand('ZSCAN', ...args);\n\n if (Array.isArray(result) && result.length === 2) {\n return [String(result[0]), result[1] as string[]];\n }\n\n return ['0', []];\n }\n\n /**\n * Override info to return string.\n */\n override async info(section?: string): Promise<string> {\n this.assertConnected();\n const result = await this.executeCommand('INFO', ...(section ? [section] : []));\n return String(result ?? '');\n }\n\n private createSingleClient(config: ConnectionConfig): RedisClientType {\n if (!isSingleConnection(config)) {\n throw new ConnectionError('Invalid single connection configuration');\n }\n\n const options: RedisClientOptions = {\n socket: {\n host: config.host ?? 'localhost',\n port: config.port ?? 6379,\n connectTimeout: config.connectTimeout ?? 10000,\n reconnectStrategy: config.retryStrategy ? (retries) => config.retryStrategy?.(retries) ?? false : this.getDefaultReconnectStrategy(),\n },\n password: config.password,\n database: config.db ?? 0,\n commandsQueueMaxLength: config.enableOfflineQueue === false ? 0 : undefined,\n };\n\n // TLS configuration\n if (config.tls?.enabled) {\n options.socket = {\n ...options.socket,\n tls: true,\n ca: config.tls.ca,\n cert: config.tls.cert,\n key: config.tls.key,\n rejectUnauthorized: config.tls.rejectUnauthorized ?? true,\n };\n }\n\n return createClient(options) as RedisClientType;\n }\n\n private createClusterClient(config: ConnectionConfig): RedisClusterType {\n if (!isClusterConnection(config)) {\n throw new ConnectionError('Invalid cluster configuration');\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const options: any = {\n rootNodes: config.nodes.map((node) => ({\n socket: {\n host: node.host,\n port: node.port,\n },\n })),\n defaults: {\n password: config.password,\n database: config.db ?? 0,\n socket: {\n connectTimeout: config.connectTimeout ?? 10000,\n reconnectStrategy: config.retryStrategy ? (retries: number) => config.retryStrategy?.(retries) ?? false : this.getDefaultReconnectStrategy(),\n },\n },\n maxCommandRedirections: config.clusterOptions?.maxRedirections ?? 16,\n useReplicas: config.clusterOptions?.scaleReads === 'slave',\n };\n\n // Add nodeAddressMap for NAT/Docker scenarios\n // Converts our natMap format to node-redis nodeAddressMap format\n if (config.clusterOptions?.natMap) {\n options.nodeAddressMap = config.clusterOptions.natMap;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return createCluster(options) as any;\n }\n\n /**\n * Creates a Redis Sentinel client using the proper createSentinel API.\n * Returns already connected sentinel instance.\n *\n * NOTE: node-redis does not support natMap/nodeAddressMap for Sentinel.\n * For Docker/NAT environments, use ioredis driver which supports sentinelOptions.natMap.\n */\n private async createSentinelClient(config: ConnectionConfig): Promise<RedisSentinelType> {\n if (!isSentinelConnection(config)) {\n throw new ConnectionError('Invalid sentinel configuration');\n }\n\n const sentinelConfig = config;\n\n // Build sentinel root nodes from config\n const sentinelRootNodes = sentinelConfig.sentinels.map((sentinel) => ({\n host: sentinel.host,\n port: sentinel.port,\n }));\n\n // Build sentinel options\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const options: any = {\n name: sentinelConfig.name,\n sentinelRootNodes,\n // Pool size for master connections\n masterPoolSize: sentinelConfig.sentinelOptions?.masterPoolSize ?? 1,\n // Pool size for replica connections (0 = no replicas)\n replicaPoolSize: sentinelConfig.sentinelOptions?.replicaPoolSize ?? 0,\n // Interval to scan for topology changes (ms)\n scanInterval: sentinelConfig.sentinelOptions?.scanInterval ?? 10000,\n // Max retries on topology change\n maxCommandRediscovers: sentinelConfig.sentinelOptions?.maxCommandRediscovers ?? 16,\n };\n\n // Node client options (for connecting to actual Redis nodes)\n const nodeClientOptions: RedisClientOptions = {\n password: sentinelConfig.password,\n database: sentinelConfig.db ?? 0,\n socket: {\n connectTimeout: sentinelConfig.connectTimeout ?? 10000,\n reconnectStrategy: sentinelConfig.retryStrategy ? (retries) => sentinelConfig.retryStrategy?.(retries) ?? false : this.getDefaultReconnectStrategy(),\n },\n };\n\n // TLS for nodes\n if (sentinelConfig.tls?.enabled) {\n nodeClientOptions.socket = {\n ...nodeClientOptions.socket,\n tls: true,\n ca: sentinelConfig.tls.ca,\n cert: sentinelConfig.tls.cert,\n key: sentinelConfig.tls.key,\n rejectUnauthorized: sentinelConfig.tls.rejectUnauthorized ?? true,\n };\n }\n\n options.nodeClientOptions = nodeClientOptions;\n\n // Sentinel client options (for connecting to sentinel instances)\n const sentinelClientOptions: RedisClientOptions = {\n socket: {\n connectTimeout: sentinelConfig.connectTimeout ?? 10000,\n },\n };\n\n // Sentinel password (if different from node password)\n if (sentinelConfig.sentinelOptions?.sentinelPassword) {\n sentinelClientOptions.password = sentinelConfig.sentinelOptions.sentinelPassword;\n }\n\n // TLS for sentinel connections\n if (sentinelConfig.sentinelOptions?.enableTLSForSentinelMode && sentinelConfig.tls?.enabled) {\n sentinelClientOptions.socket = {\n ...sentinelClientOptions.socket,\n tls: true,\n ca: sentinelConfig.tls.ca,\n cert: sentinelConfig.tls.cert,\n key: sentinelConfig.tls.key,\n rejectUnauthorized: sentinelConfig.tls.rejectUnauthorized ?? true,\n };\n }\n\n options.sentinelClientOptions = sentinelClientOptions;\n\n // Create and connect sentinel\n const sentinel = createSentinel(options);\n\n // Setup error handler before connecting\n sentinel.on('error', (error: Error) => {\n this.emit(DriverEvent.ERROR, error);\n });\n\n await sentinel.connect();\n\n return sentinel as RedisSentinelType;\n }\n\n /**\n * Safely disconnect the client, ignoring errors.\n */\n private async safeDisconnect(): Promise<void> {\n try {\n if (this.isSentinel) {\n await (this.client as RedisSentinelType)?.close();\n } else if (this.client) {\n // Single and cluster clients have disconnect()\n await (this.client as RedisClientType | RedisClusterType).disconnect();\n }\n } catch {\n // Ignore disconnect errors\n }\n }\n\n /**\n * Default reconnection strategy: exponential backoff with max 30s delay.\n * Returns a function that calculates delay based on retry count.\n */\n private getDefaultReconnectStrategy(): (retries: number) => number | false {\n return (retries: number): number | false => {\n // Max 10 retries\n if (retries > 10) {\n return false;\n }\n // Exponential backoff: 100ms, 200ms, 400ms, 800ms, 1.6s, 3.2s, 6.4s, 12.8s, 25.6s, 30s\n return Math.min(retries * RETRY_BASE_DELAY_MS * Math.pow(2, retries - 1), RETRY_MAX_DELAY_MS);\n };\n }\n\n /**\n * Get the first key from command arguments for cluster slot routing.\n * Returns undefined for commands that don't require a key.\n */\n private getFirstKey(command: string, args: string[]): string | undefined {\n const cmd = command.toUpperCase();\n\n // Commands without keys\n const noKeyCommands = new Set(['PING', 'INFO', 'DBSIZE', 'TIME', 'LASTSAVE', 'BGSAVE', 'BGREWRITEAOF', 'SLAVEOF', 'REPLICAOF', 'DEBUG', 'CONFIG', 'CLIENT', 'CLUSTER', 'COMMAND', 'FLUSHALL', 'FLUSHDB', 'SELECT', 'SWAPDB', 'PUBLISH', 'PUBSUB', 'SCRIPT', 'SLOWLOG', 'ACL', 'MODULE', 'LATENCY', 'MEMORY', 'OBJECT', 'WAIT', 'SHUTDOWN', 'SYNC', 'PSYNC', 'REPLCONF', 'ASKING', 'READONLY', 'READWRITE', 'AUTH', 'ECHO', 'QUIT']);\n\n if (noKeyCommands.has(cmd) || args.length === 0) {\n return undefined;\n }\n\n // EVAL/EVALSHA: args = [script/sha, numkeys, key1, key2, ..., arg1, arg2, ...]\n if ((cmd === 'EVAL' || cmd === 'EVALSHA') && args.length > 2) {\n const numkeys = parseInt(args[1]!, 10);\n if (numkeys > 0) {\n return args[2];\n }\n return undefined;\n }\n\n // Most commands have the key as first argument\n return args[0];\n }\n\n /**\n * Check if a command is readonly (doesn't modify data).\n * Used for cluster to route reads to replicas if enabled.\n */\n private isReadonlyCommand(command: string): boolean {\n const readonlyCommands = new Set(['GET', 'MGET', 'STRLEN', 'GETRANGE', 'SUBSTR', 'BITCOUNT', 'BITPOS', 'GETBIT', 'EXISTS', 'TYPE', 'TTL', 'PTTL', 'EXPIRETIME', 'PEXPIRETIME', 'HGET', 'HMGET', 'HGETALL', 'HKEYS', 'HVALS', 'HLEN', 'HEXISTS', 'HSCAN', 'HSTRLEN', 'HRANDFIELD', 'LINDEX', 'LLEN', 'LRANGE', 'LPOS', 'SMEMBERS', 'SISMEMBER', 'SCARD', 'SRANDMEMBER', 'SSCAN', 'SMISMEMBER', 'ZRANGE', 'ZREVRANGE', 'ZRANGEBYSCORE', 'ZREVRANGEBYSCORE', 'ZRANK', 'ZREVRANK', 'ZCOUNT', 'ZLEXCOUNT', 'ZCARD', 'ZSCORE', 'ZMSCORE', 'ZRANDMEMBER', 'ZSCAN', 'PFCOUNT', 'GEODIST', 'GEOHASH', 'GEOPOS', 'GEORADIUS_RO', 'GEORADIUSBYMEMBER_RO', 'GEOSEARCH', 'XLEN', 'XRANGE', 'XREVRANGE', 'XREAD', 'XINFO', 'XPENDING', 'SCAN', 'KEYS', 'DBSIZE', 'DEBUG', 'DUMP', 'OBJECT', 'RANDOMKEY', 'INFO', 'PING', 'TIME', 'LASTSAVE', 'ECHO']);\n\n return readonlyCommands.has(command.toUpperCase());\n }\n\n /**\n * Convert arguments to proper types for sendCommand.\n * sendCommand expects string[] but we need to handle numbers properly.\n */\n private convertArgs(args: unknown[]): string[] {\n return args.map((arg) => {\n if (arg === null || arg === undefined) {\n return '';\n }\n if (typeof arg === 'number') {\n return String(arg);\n }\n if (typeof arg === 'boolean') {\n return arg ? '1' : '0';\n }\n if (Buffer.isBuffer(arg)) {\n return arg.toString();\n }\n if (Array.isArray(arg)) {\n // Flatten arrays (for commands like MSET)\n return arg.map((item) => String(item)).join(' ');\n }\n if (typeof arg === 'object') {\n // For objects, try to serialize\n return JSON.stringify(arg);\n }\n return String(arg);\n });\n }\n\n /**\n * Apply key prefix to key-based commands.\n * This emulates ioredis keyPrefix functionality.\n */\n private applyKeyPrefix(command: string, args: string[]): string[] {\n if (!this.keyPrefix || args.length === 0) {\n return args;\n }\n\n // Commands where first argument is a key\n const singleKeyCommands = new Set(['GET', 'SET', 'DEL', 'EXISTS', 'EXPIRE', 'EXPIREAT', 'PEXPIRE', 'PEXPIREAT', 'TTL', 'PTTL', 'PERSIST', 'TYPE', 'RENAME', 'RENAMENX', 'DUMP', 'RESTORE', 'INCR', 'INCRBY', 'INCRBYFLOAT', 'DECR', 'DECRBY', 'APPEND', 'STRLEN', 'GETRANGE', 'SETRANGE', 'GETSET', 'SETNX', 'SETEX', 'PSETEX', 'GETEX', 'GETDEL', 'HGET', 'HSET', 'HSETNX', 'HDEL', 'HEXISTS', 'HGETALL', 'HINCRBY', 'HINCRBYFLOAT', 'HKEYS', 'HVALS', 'HLEN', 'HMGET', 'HMSET', 'HSCAN', 'HSTRLEN', 'HRANDFIELD', 'LPUSH', 'RPUSH', 'LPOP', 'RPOP', 'LLEN', 'LRANGE', 'LINDEX', 'LSET', 'LREM', 'LTRIM', 'LINSERT', 'LPOS', 'LPUSHX', 'RPUSHX', 'SADD', 'SREM', 'SMEMBERS', 'SISMEMBER', 'SCARD', 'SPOP', 'SRANDMEMBER', 'SSCAN', 'SMISMEMBER', 'ZADD', 'ZREM', 'ZSCORE', 'ZRANK', 'ZREVRANK', 'ZRANGE', 'ZREVRANGE', 'ZRANGEBYSCORE', 'ZREVRANGEBYSCORE', 'ZCOUNT', 'ZLEXCOUNT', 'ZCARD', 'ZINCRBY', 'ZPOPMIN', 'ZPOPMAX', 'ZMSCORE', 'ZRANDMEMBER', 'ZSCAN', 'PFADD', 'PFCOUNT', 'GEOADD', 'GEODIST', 'GEOHASH', 'GEOPOS', 'GEOSEARCH', 'SETBIT', 'GETBIT', 'BITCOUNT', 'BITPOS', 'XADD', 'XLEN', 'XRANGE', 'XREVRANGE', 'XREAD', 'XTRIM', 'XINFO', 'XDEL', 'XGROUP', 'XREADGROUP', 'XACK', 'XPENDING', 'XCLAIM', 'XAUTOCLAIM', 'WATCH', 'UNWATCH', 'OBJECT', 'DEBUG', 'MEMORY', 'COPY', 'TOUCH', 'UNLINK', 'SCAN']);\n\n // Commands where multiple arguments are keys\n const multiKeyCommands = new Set(['MGET', 'DEL', 'EXISTS', 'TOUCH', 'UNLINK', 'WATCH']);\n\n // Commands with destination key as first arg and source keys after\n const destSourceCommands = new Set(['RENAME', 'RENAMENX', 'COPY', 'BITOP', 'SINTERSTORE', 'SUNIONSTORE', 'SDIFFSTORE', 'ZUNIONSTORE', 'ZINTERSTORE', 'PFMERGE', 'GEOSEARCHSTORE', 'LMOVE', 'BLMOVE']);\n\n const cmd = command.toUpperCase();\n\n if (multiKeyCommands.has(cmd)) {\n // All args are keys\n return args.map((arg) => this.keyPrefix + arg);\n }\n\n if (destSourceCommands.has(cmd)) {\n // First arg is destination, rest might be keys\n if (args.length > 0) {\n const result = [...args];\n result[0] = this.keyPrefix + result[0];\n // For most dest/source commands, second arg is also a key\n if (args.length > 1 && ['RENAME', 'RENAMENX', 'COPY', 'LMOVE', 'BLMOVE'].includes(cmd)) {\n result[1] = this.keyPrefix + result[1];\n }\n return result;\n }\n }\n\n if (singleKeyCommands.has(cmd)) {\n // Only first arg is a key\n if (args.length > 0) {\n const result = [...args];\n result[0] = this.keyPrefix + result[0];\n return result;\n }\n }\n\n // Special case: MSET (key value key value ...)\n if (cmd === 'MSET' || cmd === 'MSETNX') {\n const result: string[] = [];\n for (let i = 0; i < args.length; i += 2) {\n result.push(this.keyPrefix + args[i]);\n const value = args[i + 1];\n if (value !== undefined) {\n result.push(value);\n }\n }\n return result;\n }\n\n return args;\n }\n\n private setupEventHandlers(): void {\n if (!this.client) {\n return;\n }\n\n // Sentinel has different event handling (already set up in createSentinelClient)\n if (this.isSentinel) {\n return;\n }\n\n this.client.on('connect', () => {\n this.emit(DriverEvent.CONNECT);\n });\n\n this.client.on('ready', () => {\n this.emit(DriverEvent.READY);\n });\n\n this.client.on('error', (error: Error) => {\n this.emit(DriverEvent.ERROR, error);\n });\n\n this.client.on('end', () => {\n this.emit(DriverEvent.END);\n });\n\n this.client.on('reconnecting', () => {\n this.emit(DriverEvent.RECONNECTING);\n });\n }\n\n /**\n * Gets underlying node-redis client.\n * For advanced usage only.\n */\n getClient(): NodeRedisClient | null {\n return this.client;\n }\n\n /**\n * Returns true if connected to Redis Sentinel.\n */\n isSentinelMode(): boolean {\n return this.isSentinel;\n }\n}\n\n/**\n * Node-Redis pipeline adapter.\n *\n * Note: node-redis doesn't have separate pipeline,\n * so we use multi() for batching.\n */\nclass NodeRedisPipelineAdapter implements IPipeline {\n protected commands: Array<{ command: string; args: unknown[] }> = [];\n\n constructor(\n protected readonly client: RedisClientType | RedisClusterType,\n protected readonly keyPrefix: string = '',\n ) {}\n\n async exec(): Promise<Array<[Error | null, unknown]>> {\n const multi = (this.client as RedisClientType).multi();\n\n for (const { command, args } of this.commands) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (multi as any).addCommand([command, ...args.map(String)]);\n }\n\n try {\n const results = await multi.exec();\n return (results as unknown[]).map((result) => [null, result]);\n } catch (error) {\n return [[error as Error, null]];\n }\n }\n\n protected prefixKey(key: string): string {\n return this.keyPrefix ? this.keyPrefix + key : key;\n }\n\n get(key: string): this {\n this.commands.push({ command: 'GET', args: [this.prefixKey(key)] });\n return this;\n }\n\n set(key: string, value: string, options?: { ex?: number; px?: number }): this {\n const args: unknown[] = [this.prefixKey(key), value];\n if (options?.ex) {\n args.push('EX', options.ex);\n } else if (options?.px) {\n args.push('PX', options.px);\n }\n this.commands.push({ command: 'SET', args });\n return this;\n }\n\n del(...keys: string[]): this {\n this.commands.push({ command: 'DEL', args: keys.map((k) => this.prefixKey(k)) });\n return this;\n }\n\n mget(...keys: string[]): this {\n this.commands.push({ command: 'MGET', args: keys.map((k) => this.prefixKey(k)) });\n return this;\n }\n\n mset(data: Record<string, string>): this {\n const args: unknown[] = [];\n for (const [key, value] of Object.entries(data)) {\n args.push(this.prefixKey(key), value);\n }\n this.commands.push({ command: 'MSET', args });\n return this;\n }\n\n expire(key: string, seconds: number): this {\n this.commands.push({ command: 'EXPIRE', args: [this.prefixKey(key), seconds] });\n return this;\n }\n\n ttl(key: string): this {\n this.commands.push({ command: 'TTL', args: [this.prefixKey(key)] });\n return this;\n }\n\n incr(key: string): this {\n this.commands.push({ command: 'INCR', args: [this.prefixKey(key)] });\n return this;\n }\n\n incrby(key: string, increment: number): this {\n this.commands.push({ command: 'INCRBY', args: [this.prefixKey(key), increment] });\n return this;\n }\n\n hget(key: string, field: string): this {\n this.commands.push({ command: 'HGET', args: [this.prefixKey(key), field] });\n return this;\n }\n\n hset(key: string, field: string, value: string): this {\n this.commands.push({ command: 'HSET', args: [this.prefixKey(key), field, value] });\n return this;\n }\n\n hmset(key: string, data: Record<string, string>): this {\n const args: unknown[] = [this.prefixKey(key)];\n for (const [field, value] of Object.entries(data)) {\n args.push(field, value);\n }\n this.commands.push({ command: 'HMSET', args });\n return this;\n }\n\n hgetall(key: string): this {\n this.commands.push({ command: 'HGETALL', args: [this.prefixKey(key)] });\n return this;\n }\n\n lpush(key: string, ...values: string[]): this {\n this.commands.push({ command: 'LPUSH', args: [this.prefixKey(key), ...values] });\n return this;\n }\n\n rpush(key: string, ...values: string[]): this {\n this.commands.push({ command: 'RPUSH', args: [this.prefixKey(key), ...values] });\n return this;\n }\n\n sadd(key: string, ...members: string[]): this {\n this.commands.push({ command: 'SADD', args: [this.prefixKey(key), ...members] });\n return this;\n }\n\n srem(key: string, ...members: string[]): this {\n this.commands.push({ command: 'SREM', args: [this.prefixKey(key), ...members] });\n return this;\n }\n\n zadd(key: string, ...args: Array<number | string>): this {\n this.commands.push({ command: 'ZADD', args: [this.prefixKey(key), ...args] });\n return this;\n }\n\n zrem(key: string, ...members: string[]): this {\n this.commands.push({ command: 'ZREM', args: [this.prefixKey(key), ...members] });\n return this;\n }\n}\n\n/**\n * Node-Redis multi/exec adapter.\n */\nclass NodeRedisMultiAdapter extends NodeRedisPipelineAdapter implements IMulti {\n constructor(client: RedisClientType | RedisClusterType, keyPrefix: string = '') {\n super(client, keyPrefix);\n }\n\n discard(): void {\n // Clear queued commands\n this.commands = [];\n }\n}\n\n/**\n * Node-Redis Sentinel pipeline adapter.\n * Uses sentinel.use() to get a client from the pool.\n */\nclass NodeRedisSentinelPipelineAdapter implements IPipeline {\n protected commands: Array<{ command: string; args: unknown[] }> = [];\n\n constructor(\n protected readonly sentinel: RedisSentinelType,\n protected readonly keyPrefix: string = '',\n ) {}\n\n async exec(): Promise<Array<[Error | null, unknown]>> {\n return await this.sentinel.use(async (client) => {\n const multi = client.multi();\n\n for (const { command, args } of this.commands) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (multi as any).addCommand([command, ...args.map(String)]);\n }\n\n try {\n const results = await multi.exec();\n return (results as unknown[]).map((result) => [null, result] as [Error | null, unknown]);\n } catch (error) {\n return [[error as Error, null]] as Array<[Error | null, unknown]>;\n }\n });\n }\n\n protected prefixKey(key: string): string {\n return this.keyPrefix ? this.keyPrefix + key : key;\n }\n\n get(key: string): this {\n this.commands.push({ command: 'GET', args: [this.prefixKey(key)] });\n return this;\n }\n\n set(key: string, value: string, options?: { ex?: number; px?: number }): this {\n const args: unknown[] = [this.prefixKey(key), value];\n if (options?.ex) {\n args.push('EX', options.ex);\n } else if (options?.px) {\n args.push('PX', options.px);\n }\n this.commands.push({ command: 'SET', args });\n return this;\n }\n\n del(...keys: string[]): this {\n this.commands.push({ command: 'DEL', args: keys.map((k) => this.prefixKey(k)) });\n return this;\n }\n\n mget(...keys: string[]): this {\n this.commands.push({ command: 'MGET', args: keys.map((k) => this.prefixKey(k)) });\n return this;\n }\n\n mset(data: Record<string, string>): this {\n const args: unknown[] = [];\n for (const [key, value] of Object.entries(data)) {\n args.push(this.prefixKey(key), value);\n }\n this.commands.push({ command: 'MSET', args });\n return this;\n }\n\n expire(key: string, seconds: number): this {\n this.commands.push({ command: 'EXPIRE', args: [this.prefixKey(key), seconds] });\n return this;\n }\n\n ttl(key: string): this {\n this.commands.push({ command: 'TTL', args: [this.prefixKey(key)] });\n return this;\n }\n\n incr(key: string): this {\n this.commands.push({ command: 'INCR', args: [this.prefixKey(key)] });\n return this;\n }\n\n incrby(key: string, increment: number): this {\n this.commands.push({ command: 'INCRBY', args: [this.prefixKey(key), increment] });\n return this;\n }\n\n hget(key: string, field: string): this {\n this.commands.push({ command: 'HGET', args: [this.prefixKey(key), field] });\n return this;\n }\n\n hset(key: string, field: string, value: string): this {\n this.commands.push({ command: 'HSET', args: [this.prefixKey(key), field, value] });\n return this;\n }\n\n hmset(key: string, data: Record<string, string>): this {\n const args: unknown[] = [this.prefixKey(key)];\n for (const [field, value] of Object.entries(data)) {\n args.push(field, value);\n }\n this.commands.push({ command: 'HMSET', args });\n return this;\n }\n\n hgetall(key: string): this {\n this.commands.push({ command: 'HGETALL', args: [this.prefixKey(key)] });\n return this;\n }\n\n lpush(key: string, ...values: string[]): this {\n this.commands.push({ command: 'LPUSH', args: [this.prefixKey(key), ...values] });\n return this;\n }\n\n rpush(key: string, ...values: string[]): this {\n this.commands.push({ command: 'RPUSH', args: [this.prefixKey(key), ...values] });\n return this;\n }\n\n sadd(key: string, ...members: string[]): this {\n this.commands.push({ command: 'SADD', args: [this.prefixKey(key), ...members] });\n return this;\n }\n\n srem(key: string, ...members: string[]): this {\n this.commands.push({ command: 'SREM', args: [this.prefixKey(key), ...members] });\n return this;\n }\n\n zadd(key: string, ...args: Array<number | string>): this {\n this.commands.push({ command: 'ZADD', args: [this.prefixKey(key), ...args] });\n return this;\n }\n\n zrem(key: string, ...members: string[]): this {\n this.commands.push({ command: 'ZREM', args: [this.prefixKey(key), ...members] });\n return this;\n }\n}\n\n/**\n * Node-Redis Sentinel multi/exec adapter.\n */\nclass NodeRedisSentinelMultiAdapter extends NodeRedisSentinelPipelineAdapter implements IMulti {\n constructor(sentinel: RedisSentinelType, keyPrefix: string = '') {\n super(sentinel, keyPrefix);\n }\n\n discard(): void {\n this.commands = [];\n }\n}\n","import { IRedisDriver } from '../../interfaces';\nimport { RedisXError, ErrorCode } from '../../errors';\nimport { ConnectionConfig, DriverType } from '../../types';\nimport { IoRedisAdapter } from '../infrastructure/ioredis.adapter';\nimport { NodeRedisAdapter } from '../infrastructure/node-redis.adapter';\n\n/**\n * Driver factory options.\n */\nexport interface IDriverFactoryOptions {\n /**\n * Driver type to create.\n * @default 'ioredis'\n */\n type?: DriverType;\n\n /**\n * Enable operation logging.\n * @default false\n */\n enableLogging?: boolean;\n}\n\n/**\n * Factory for creating Redis drivers.\n *\n * Creates driver instances based on configuration.\n * Drivers are interchangeable - switching between ioredis and node-redis\n * requires no code changes.\n *\n * @example\n * ```typescript\n * // Create ioredis driver\n * const driver = createDriver(\n * { host: 'localhost', port: 6379 },\n * { type: 'ioredis' }\n * );\n *\n * // Switch to node-redis (same interface)\n * const driver = createDriver(\n * { host: 'localhost', port: 6379 },\n * { type: 'node-redis' }\n * );\n * ```\n */\nexport function createDriver(config: ConnectionConfig, options?: IDriverFactoryOptions): IRedisDriver {\n const driverType = options?.type ?? 'ioredis';\n const enableLogging = options?.enableLogging ?? false;\n\n switch (driverType) {\n case 'ioredis':\n return new IoRedisAdapter(config, { enableLogging });\n\n case 'node-redis':\n return new NodeRedisAdapter(config, { enableLogging });\n\n default:\n throw new RedisXError(`Unsupported driver type: ${driverType}. Supported types: ioredis, node-redis`, ErrorCode.CFG_INVALID, undefined, { driverType });\n }\n}\n\n/**\n * Driver factory class for dependency injection.\n *\n * @example\n * ```typescript\n * @Module({\n * providers: [\n * {\n * provide: DRIVER_FACTORY,\n * useClass: DriverFactory,\n * },\n * ],\n * })\n * export class MyModule {}\n * ```\n */\nexport class DriverFactory {\n /**\n * Creates a driver instance.\n */\n create(config: ConnectionConfig, options?: IDriverFactoryOptions): IRedisDriver {\n return createDriver(config, options);\n }\n\n /**\n * Gets supported driver types.\n */\n getSupportedTypes(): DriverType[] {\n return ['ioredis', 'node-redis'];\n }\n\n /**\n * Checks if driver type is supported.\n */\n isSupported(type: string): type is DriverType {\n return type === 'ioredis' || type === 'node-redis';\n }\n\n /**\n * Gets default driver type.\n */\n getDefaultType(): DriverType {\n return 'ioredis';\n }\n}\n\n/**\n * Creates multiple drivers from configurations.\n *\n * @example\n * ```typescript\n * const drivers = createDrivers({\n * default: { host: 'localhost', port: 6379 },\n * cache: { host: 'cache.local', port: 6379 },\n * });\n * ```\n */\nexport function createDrivers(configs: Record<string, ConnectionConfig>, options?: IDriverFactoryOptions): Record<string, IRedisDriver> {\n const drivers: Record<string, IRedisDriver> = {};\n\n for (const [name, config] of Object.entries(configs)) {\n drivers[name] = createDriver(config, options);\n }\n\n return drivers;\n}\n\n/**\n * Driver type detection helper.\n * Attempts to detect available driver library.\n */\nexport function detectAvailableDriver(): DriverType | null {\n try {\n require.resolve('ioredis');\n return 'ioredis';\n } catch {\n try {\n require.resolve('redis');\n return 'node-redis';\n } catch {\n return null;\n }\n }\n}\n\n/**\n * Gets recommended driver type based on environment.\n */\nexport function getRecommendedDriver(): DriverType {\n // ioredis is the recommended default due to:\n // - More battle-tested in production\n // - Better cluster support\n // - More features (Lua script caching, etc.)\n return 'ioredis';\n}\n","/**\n * Dependency injection tokens for NestJS RedisX.\n */\n\n/**\n * Token for injecting module options.\n * Used for synchronous and asynchronous configuration.\n */\nexport const REDIS_MODULE_OPTIONS = Symbol.for('REDIS_MODULE_OPTIONS');\n\n/**\n * Token for injecting default Redis client.\n * Points to the 'default' named client.\n */\nexport const REDIS_CLIENT = Symbol.for('REDIS_CLIENT');\n\n/**\n * Token for injecting Redis clients map.\n * Contains all registered named clients.\n */\nexport const REDIS_CLIENTS_MAP = Symbol.for('REDIS_CLIENTS_MAP');\n\n/**\n * Token for injecting client manager.\n * Manages lifecycle of all Redis clients.\n */\nexport const CLIENT_MANAGER = Symbol.for('CLIENT_MANAGER');\n\n/**\n * Token for injecting Redis driver.\n * Abstracts ioredis/node-redis implementation.\n */\nexport const REDIS_DRIVER = Symbol.for('REDIS_DRIVER');\n\n/**\n * Token for injecting RedisX configuration.\n * Contains module-level and global configuration.\n */\nexport const REDISX_CONFIG = Symbol.for('REDISX_CONFIG');\n\n/**\n * Token for injecting plugin registry.\n * Manages plugin lifecycle and dependencies.\n */\nexport const PLUGIN_REGISTRY = Symbol.for('PLUGIN_REGISTRY');\n\n/**\n * Token for injecting registered plugins array.\n * Used internally by PluginRegistryService.\n */\nexport const REGISTERED_PLUGINS = Symbol.for('REGISTERED_PLUGINS');\n\n/**\n * Token for injecting logger.\n * Provides structured logging for RedisX.\n */\nexport const REDISX_LOGGER = Symbol.for('REDISX_LOGGER');\n\n/**\n * Creates a token for named Redis client.\n * @param name - Client name\n * @returns Injection token\n *\n * @example\n * ```typescript\n * @Injectable()\n * export class MyService {\n * constructor(\n * @Inject(getClientToken('cache'))\n * private readonly cacheClient: IRedisClient\n * ) {}\n * }\n * ```\n */\nexport function getClientToken(name: string): symbol {\n return Symbol.for(`REDIS_CLIENT:${name}`);\n}\n\n/**\n * Creates a token for named driver.\n * @param name - Driver name\n * @returns Injection token\n */\nexport function getDriverToken(name: string): symbol {\n return Symbol.for(`REDIS_DRIVER:${name}`);\n}\n\n/**\n * Default client name.\n */\nexport const DEFAULT_CLIENT_NAME = 'default';\n\n/**\n * Default driver type.\n */\nexport const DEFAULT_DRIVER_TYPE = 'ioredis';\n\n/**\n * Module name for logging.\n */\nexport const MODULE_NAME = 'RedisXModule';\n\n/**\n * Default connection timeout (ms).\n */\nexport const DEFAULT_CONNECTION_TIMEOUT = 10000;\n\n/**\n * Default command timeout (ms).\n */\nexport const DEFAULT_COMMAND_TIMEOUT = 5000;\n\n/**\n * Default graceful shutdown timeout (ms).\n */\nexport const DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT = 5000;\n\n/**\n * Token for async clients initialization.\n * Used internally to ensure clients are created before injection.\n */\nexport const REDIS_CLIENTS_INITIALIZATION = Symbol.for('REDIS_CLIENTS_INITIALIZATION');\n\n/**\n * Default health check interval (ms).\n */\nexport const DEFAULT_HEALTH_CHECK_INTERVAL = 30000;\n","import { IRedisDriver } from '../../../interfaces';\nimport { ConnectionConfig, IClientMetadata } from '../../../types';\nimport { IHealthStatus, IConnectionStats, IReconnectionOptions } from '../types/health.types';\n\n/**\n * Redis driver manager interface.\n *\n * Manages multiple Redis driver connections with:\n * - Lazy connection (connect on first use)\n * - Automatic reconnection with exponential backoff\n * - Health monitoring\n * - Graceful shutdown\n * - Connection pooling\n *\n * @example\n * ```typescript\n * @Injectable()\n * export class CacheService {\n * constructor(\n * @Inject(CLIENT_MANAGER) private readonly clientManager: IRedisDriverManager\n * ) {}\n *\n * async get(key: string): Promise<string | null> {\n * const driver = await this.clientManager.getClient('cache');\n * return driver.get(key);\n * }\n * }\n * ```\n */\nexport interface IRedisDriverManager {\n /**\n * Gets Redis driver by name.\n *\n * Performs lazy connection on first access.\n * Subsequent calls return the same connected driver.\n *\n * @param name - Client name (default: 'default')\n * @returns Redis driver instance\n * @throws Error if client not found or connection fails\n *\n * @example\n * ```typescript\n * const driver = await manager.getClient('cache');\n * await driver.set('key', 'value');\n * ```\n */\n getClient(name?: string): Promise<IRedisDriver>;\n\n /**\n * Creates and registers a new Redis driver.\n *\n * @param name - Unique client name\n * @param config - Redis connection configuration\n * @param options - Optional client options\n * @returns Created driver instance\n * @throws Error if client with this name already exists\n *\n * @example\n * ```typescript\n * const driver = await manager.createClient('sessions', {\n * type: 'single',\n * host: 'localhost',\n * port: 6379,\n * db: 1,\n * });\n * ```\n */\n createClient(\n name: string,\n config: ConnectionConfig,\n options?: {\n reconnection?: IReconnectionOptions;\n metadata?: Partial<IClientMetadata>;\n },\n ): Promise<IRedisDriver>;\n\n /**\n * Checks if client exists.\n *\n * @param name - Client name\n * @returns true if client is registered\n */\n hasClient(name: string): boolean;\n\n /**\n * Gets all registered client names.\n *\n * @returns Array of client names\n */\n getClientNames(): string[];\n\n /**\n * Closes specific client connection.\n *\n * Gracefully disconnects and removes client from registry.\n *\n * @param name - Client name\n * @throws Error if client not found\n *\n * @example\n * ```typescript\n * await manager.closeClient('cache');\n * ```\n */\n closeClient(name: string): Promise<void>;\n\n /**\n * Closes all client connections.\n *\n * Performs graceful shutdown of all clients in parallel.\n * Safe to call multiple times.\n *\n * @example\n * ```typescript\n * await manager.closeAll();\n * ```\n */\n closeAll(): Promise<void>;\n\n /**\n * Performs health check on client(s).\n *\n * Executes PING command and measures latency.\n *\n * @param name - Client name (if omitted, checks all clients)\n * @returns Health status for specified client(s)\n *\n * @example\n * ```typescript\n * // Check single client\n * const health = await manager.healthCheck('cache');\n * if (!health.healthy) {\n * logger.error('Cache unhealthy', health);\n * }\n *\n * // Check all clients\n * const allHealth = await manager.healthCheck();\n * ```\n */\n healthCheck(name?: string): Promise<IHealthStatus | IHealthStatus[]>;\n\n /**\n * Gets connection statistics.\n *\n * Returns aggregate stats for all clients.\n *\n * @returns Connection statistics\n *\n * @example\n * ```typescript\n * const stats = manager.getStats();\n * console.log(`Connected: ${stats.connectedClients}/${stats.totalClients}`);\n * ```\n */\n getStats(): IConnectionStats;\n\n /**\n * Gets client metadata.\n *\n * @param name - Client name\n * @returns Client metadata\n * @throws Error if client not found\n */\n getMetadata(name: string): IClientMetadata;\n\n /**\n * Updates client metadata.\n *\n * @param name - Client name\n * @param metadata - Partial metadata to merge\n */\n updateMetadata(name: string, metadata: Partial<IClientMetadata>): void;\n\n /**\n * Registers event listener for all clients.\n *\n * @param event - Event name\n * @param handler - Event handler\n *\n * @example\n * ```typescript\n * manager.on('manager:reconnecting', (data) => {\n * logger.warn(`Client ${data.name} reconnecting...`);\n * });\n * ```\n */\n on(event: ManagerEvent, handler: ManagerEventHandler): void;\n\n /**\n * Unregisters event listener.\n *\n * @param event - Event name\n * @param handler - Event handler\n */\n off(event: ManagerEvent, handler: ManagerEventHandler): void;\n}\n\n/**\n * Manager-specific events.\n */\nexport enum ManagerEvent {\n /**\n * Client connected.\n */\n CONNECTED = 'manager:connected',\n\n /**\n * Client disconnected.\n */\n DISCONNECTED = 'manager:disconnected',\n\n /**\n * Client reconnecting.\n */\n RECONNECTING = 'manager:reconnecting',\n\n /**\n * Client error.\n */\n ERROR = 'manager:error',\n\n /**\n * Client created.\n */\n CREATED = 'manager:created',\n\n /**\n * Client removed.\n */\n REMOVED = 'manager:removed',\n}\n\n/**\n * Manager event data.\n */\nexport interface IManagerEventData {\n /**\n * Client name.\n */\n name: string;\n\n /**\n * Event timestamp.\n */\n timestamp: Date;\n\n /**\n * Error if any.\n */\n error?: Error;\n\n /**\n * Additional context.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Manager event handler.\n */\nexport type ManagerEventHandler = (data: IManagerEventData) => void;\n","import { Provider } from '@nestjs/common';\n\nimport { RedisClientManager } from '../client';\nimport { IRedisDriver } from '../interfaces';\nimport { REDIS_MODULE_OPTIONS, CLIENT_MANAGER, REDIS_DRIVER, REDIS_CLIENTS_INITIALIZATION, getClientToken, DEFAULT_CLIENT_NAME } from '../shared/constants';\nimport { IRedisModuleOptions, IRedisModuleAsyncOptions, IRedisModuleOptionsFactory, ConnectionConfig } from '../types';\n\n/**\n * Type guard to check if clients config is a single connection.\n */\nfunction isISingleConnectionConfig(clients: ConnectionConfig | Record<string, ConnectionConfig>): clients is ConnectionConfig {\n return 'type' in clients || 'host' in clients || 'nodes' in clients || 'sentinels' in clients;\n}\n\n/**\n * Creates Redis providers for synchronous configuration.\n *\n * @param options - Redis module options\n * @returns Array of providers\n */\nexport function createRedisProviders(options: IRedisModuleOptions): Provider[] {\n return [\n // Module options provider\n {\n provide: REDIS_MODULE_OPTIONS,\n useValue: options,\n },\n // Client manager provider\n {\n provide: CLIENT_MANAGER,\n useFactory: () => {\n return new RedisClientManager();\n },\n },\n // Client providers (one per connection)\n ...createClientProviders(options),\n // REDIS_DRIVER alias to default client (for backwards compatibility and plugin usage)\n {\n provide: REDIS_DRIVER,\n useFactory: async (manager: RedisClientManager): Promise<IRedisDriver> => {\n return manager.getClient(DEFAULT_CLIENT_NAME);\n },\n inject: [CLIENT_MANAGER],\n },\n ];\n}\n\n/**\n * Creates Redis providers for asynchronous configuration.\n *\n * @param options - Async Redis module options\n * @returns Array of providers\n */\nexport function createAsyncProviders(options: IRedisModuleAsyncOptions): Provider[] {\n const providers: Provider[] = [\n // Client manager provider\n {\n provide: CLIENT_MANAGER,\n useFactory: () => {\n return new RedisClientManager();\n },\n },\n ];\n\n if (options.useExisting || options.useFactory) {\n // Options provider using factory\n providers.push(createAsyncOptionsProvider(options));\n }\n\n if (options.useClass) {\n // Options provider using class\n providers.push(\n {\n provide: options.useClass,\n useClass: options.useClass,\n },\n createAsyncOptionsProvider(options),\n );\n }\n\n // Async client providers\n providers.push(...createAsyncClientProviders(options));\n\n // REDIS_DRIVER alias to default client (depends on initialization)\n providers.push({\n provide: REDIS_DRIVER,\n useFactory: async (\n manager: RedisClientManager,\n _init: void, // Ensure clients are initialized first\n ): Promise<IRedisDriver> => {\n return manager.getClient(DEFAULT_CLIENT_NAME);\n },\n inject: [CLIENT_MANAGER, REDIS_CLIENTS_INITIALIZATION],\n });\n\n return providers;\n}\n\n/**\n * Creates plugin providers from resolved options (for async configuration).\n * Must be called after REDIS_MODULE_OPTIONS is resolved.\n *\n * @param moduleOptions - Resolved Redis module options\n * @returns Array of providers and exports\n */\nexport function createPluginProviders(moduleOptions: IRedisModuleOptions): {\n providers: Provider[];\n exports: Array<string | symbol | Provider>;\n} {\n const providers: Provider[] = [];\n const exports: Array<string | symbol | Provider> = [];\n\n if (moduleOptions.plugins && moduleOptions.plugins.length > 0) {\n moduleOptions.plugins.forEach((plugin) => {\n if (plugin.getProviders) {\n const pluginProviders = plugin.getProviders();\n if (Array.isArray(pluginProviders)) {\n providers.push(...pluginProviders);\n }\n }\n if (plugin.getExports) {\n const pluginExports = plugin.getExports();\n if (Array.isArray(pluginExports)) {\n exports.push(...pluginExports);\n }\n }\n });\n }\n\n return { providers, exports };\n}\n\n/**\n * Creates options provider for async configuration.\n */\nfunction createAsyncOptionsProvider(options: IRedisModuleAsyncOptions): Provider {\n if (options.useFactory) {\n return {\n provide: REDIS_MODULE_OPTIONS,\n useFactory: options.useFactory,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n inject: (options.inject || []) as any[],\n };\n }\n\n if (options.useExisting) {\n return {\n provide: REDIS_MODULE_OPTIONS,\n useFactory: async (optionsFactory: IRedisModuleOptionsFactory) => await optionsFactory.createRedisModuleOptions(),\n inject: [options.useExisting],\n };\n }\n\n if (options.useClass) {\n return {\n provide: REDIS_MODULE_OPTIONS,\n useFactory: async (optionsFactory: IRedisModuleOptionsFactory) => await optionsFactory.createRedisModuleOptions(),\n inject: [options.useClass],\n };\n }\n\n throw new Error('Invalid async options configuration');\n}\n\n/**\n * Creates client providers from module options.\n */\nfunction createClientProviders(options: IRedisModuleOptions): Provider[] {\n const providers: Provider[] = [];\n const driverType = options.global?.driver ?? 'ioredis';\n\n // Handle single connection or multiple named connections\n if (isISingleConnectionConfig(options.clients)) {\n // Single connection config\n providers.push({\n provide: getClientToken(DEFAULT_CLIENT_NAME),\n useFactory: async (manager: RedisClientManager) => {\n return manager.createClient(DEFAULT_CLIENT_NAME, options.clients as ConnectionConfig, { driverType });\n },\n inject: [CLIENT_MANAGER],\n });\n } else {\n // Multiple named connections\n const configs = options.clients;\n\n for (const [name, config] of Object.entries(configs)) {\n providers.push({\n provide: getClientToken(name),\n useFactory: async (manager: RedisClientManager) => {\n return manager.createClient(name, config, { driverType });\n },\n inject: [CLIENT_MANAGER],\n });\n }\n }\n\n return providers;\n}\n\n/**\n * Creates async client providers.\n */\nfunction createAsyncClientProviders(_options: IRedisModuleAsyncOptions): Provider[] {\n return [\n {\n provide: REDIS_CLIENTS_INITIALIZATION,\n useFactory: async (moduleOptions: IRedisModuleOptions, manager: RedisClientManager): Promise<void> => {\n const driverType = moduleOptions.global?.driver ?? 'ioredis';\n\n // Initialize clients from async options\n if (isISingleConnectionConfig(moduleOptions.clients)) {\n // Single connection\n await manager.createClient(DEFAULT_CLIENT_NAME, moduleOptions.clients, { driverType });\n } else {\n // Multiple connections\n const configs = moduleOptions.clients;\n\n for (const [name, config] of Object.entries(configs)) {\n await manager.createClient(name, config, { driverType });\n }\n }\n },\n inject: [REDIS_MODULE_OPTIONS, CLIENT_MANAGER],\n },\n {\n provide: getClientToken(DEFAULT_CLIENT_NAME),\n useFactory: async (\n manager: RedisClientManager,\n _init: void, // Ensure initialization completes first\n ): Promise<IRedisDriver> => {\n // getClient will return the initialized client\n return manager.getClient(DEFAULT_CLIENT_NAME);\n },\n inject: [CLIENT_MANAGER, REDIS_CLIENTS_INITIALIZATION],\n },\n ];\n}\n\n/**\n * Creates feature providers for specific client.\n *\n * @param name - Client name\n * @returns Array of providers\n */\nexport function createFeatureProviders(name: string): Provider[] {\n return [\n {\n provide: getClientToken(name),\n useFactory: async (manager: RedisClientManager): Promise<IRedisDriver> => {\n return manager.getClient(name);\n },\n inject: [CLIENT_MANAGER],\n },\n ];\n}\n","import { Injectable, Inject, OnModuleDestroy } from '@nestjs/common';\n\nimport { RedisClientManager } from '../client';\nimport { IRedisDriver, ISetOptions, IScanOptions, IPipeline, IMulti } from '../interfaces';\nimport { CLIENT_MANAGER, DEFAULT_CLIENT_NAME } from '../shared/constants';\n\n/**\n * Redis service - convenient wrapper over RedisClientManager.\n *\n * Provides direct access to Redis operations without manual client management.\n * All methods are proxied to the default Redis driver.\n *\n * @example\n * ```typescript\n * @Injectable()\n * export class UserService {\n * constructor(private readonly redis: RedisService) {}\n *\n * async cacheUser(id: string, user: User): Promise<void> {\n * await this.redis.set(`user:${id}`, JSON.stringify(user), { ex: 3600 });\n * }\n *\n * async getUser(id: string): Promise<User | null> {\n * const data = await this.redis.get(`user:${id}`);\n * return data ? JSON.parse(data) : null;\n * }\n *\n * async deleteUser(id: string): Promise<void> {\n * await this.redis.del(`user:${id}`);\n * }\n * }\n * ```\n */\n@Injectable()\nexport class RedisService implements OnModuleDestroy {\n /**\n * Default Redis driver instance.\n */\n private driver: IRedisDriver | null = null;\n\n constructor(\n @Inject(CLIENT_MANAGER)\n private readonly clientManager: RedisClientManager,\n ) {}\n\n /**\n * Gets default Redis driver.\n * Lazy loads on first access.\n */\n private async getDriver(): Promise<IRedisDriver> {\n if (!this.driver) {\n this.driver = await this.clientManager.getClient(DEFAULT_CLIENT_NAME);\n }\n return this.driver;\n }\n\n /**\n * Gets a named Redis client.\n *\n * @param name - Client name\n * @returns Redis driver instance\n *\n * @example\n * ```typescript\n * const sessions = await this.redis.getClient('sessions');\n * await sessions.set('session:123', data);\n * ```\n */\n async getClient(name: string = DEFAULT_CLIENT_NAME): Promise<IRedisDriver> {\n return this.clientManager.getClient(name);\n }\n\n /**\n * Checks if default client is connected.\n */\n async isConnected(): Promise<boolean> {\n const driver = await this.getDriver();\n return driver.isConnected();\n }\n\n /**\n * Pings Redis server.\n */\n async ping(message?: string): Promise<string> {\n const driver = await this.getDriver();\n return driver.ping(message);\n }\n\n /**\n * Selects database.\n */\n async select(db: number): Promise<void> {\n const driver = await this.getDriver();\n return driver.select(db);\n }\n\n /**\n * Gets value by key.\n */\n async get(key: string): Promise<string | null> {\n const driver = await this.getDriver();\n return driver.get(key);\n }\n\n /**\n * Sets key-value pair with optional TTL.\n */\n async set(key: string, value: string, options?: ISetOptions): Promise<'OK' | null> {\n const driver = await this.getDriver();\n return driver.set(key, value, options);\n }\n\n /**\n * Gets multiple values.\n */\n async mget(...keys: string[]): Promise<Array<string | null>> {\n const driver = await this.getDriver();\n return driver.mget(...keys);\n }\n\n /**\n * Sets multiple key-value pairs.\n */\n async mset(data: Record<string, string>): Promise<'OK'> {\n const driver = await this.getDriver();\n return driver.mset(data);\n }\n\n /**\n * Sets value only if key doesn't exist.\n */\n async setnx(key: string, value: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.setnx(key, value);\n }\n\n /**\n * Sets value with expiration in seconds.\n */\n async setex(key: string, seconds: number, value: string): Promise<'OK'> {\n const driver = await this.getDriver();\n return driver.setex(key, seconds, value);\n }\n\n /**\n * Gets value and deletes key.\n */\n async getdel(key: string): Promise<string | null> {\n const driver = await this.getDriver();\n return driver.getdel(key);\n }\n\n /**\n * Gets value and sets expiration.\n */\n async getex(key: string, options: { ex?: number; px?: number }): Promise<string | null> {\n const driver = await this.getDriver();\n return driver.getex(key, options);\n }\n\n /**\n * Increments integer value by 1.\n */\n async incr(key: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.incr(key);\n }\n\n /**\n * Increments integer value by amount.\n */\n async incrby(key: string, increment: number): Promise<number> {\n const driver = await this.getDriver();\n return driver.incrby(key, increment);\n }\n\n /**\n * Decrements integer value by 1.\n */\n async decr(key: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.decr(key);\n }\n\n /**\n * Decrements integer value by amount.\n */\n async decrby(key: string, decrement: number): Promise<number> {\n const driver = await this.getDriver();\n return driver.decrby(key, decrement);\n }\n\n /**\n * Appends value to key.\n */\n async append(key: string, value: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.append(key, value);\n }\n\n /**\n * Deletes one or more keys.\n */\n async del(...keys: string[]): Promise<number> {\n const driver = await this.getDriver();\n return driver.del(...keys);\n }\n\n /**\n * Checks if keys exist.\n */\n async exists(...keys: string[]): Promise<number> {\n const driver = await this.getDriver();\n return driver.exists(...keys);\n }\n\n /**\n * Sets TTL on key (seconds).\n */\n async expire(key: string, seconds: number): Promise<number> {\n const driver = await this.getDriver();\n return driver.expire(key, seconds);\n }\n\n /**\n * Sets TTL on key (milliseconds).\n */\n async pexpire(key: string, milliseconds: number): Promise<number> {\n const driver = await this.getDriver();\n return driver.pexpire(key, milliseconds);\n }\n\n /**\n * Sets absolute expiration time.\n */\n async expireat(key: string, timestamp: number): Promise<number> {\n const driver = await this.getDriver();\n return driver.expireat(key, timestamp);\n }\n\n /**\n * Gets TTL of key (seconds).\n */\n async ttl(key: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.ttl(key);\n }\n\n /**\n * Gets TTL of key (milliseconds).\n */\n async pttl(key: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.pttl(key);\n }\n\n /**\n * Removes expiration from key.\n */\n async persist(key: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.persist(key);\n }\n\n /**\n * Renames key.\n */\n async rename(key: string, newKey: string): Promise<'OK'> {\n const driver = await this.getDriver();\n return driver.rename(key, newKey);\n }\n\n /**\n * Renames key only if new key doesn't exist.\n */\n async renamenx(key: string, newKey: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.renamenx(key, newKey);\n }\n\n /**\n * Gets type of value stored at key.\n */\n async type(key: string): Promise<string> {\n const driver = await this.getDriver();\n return driver.type(key);\n }\n\n /**\n * Scans keys matching pattern.\n */\n async scan(cursor: number, options?: IScanOptions): Promise<[string, string[]]> {\n const driver = await this.getDriver();\n return driver.scan(cursor, options);\n }\n\n /**\n * Gets hash field value.\n */\n async hget(key: string, field: string): Promise<string | null> {\n const driver = await this.getDriver();\n return driver.hget(key, field);\n }\n\n /**\n * Sets hash field value.\n */\n async hset(key: string, field: string, value: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.hset(key, field, value);\n }\n\n /**\n * Sets multiple hash fields.\n */\n async hmset(key: string, data: Record<string, string>): Promise<'OK'> {\n const driver = await this.getDriver();\n return driver.hmset(key, data);\n }\n\n /**\n * Gets multiple hash field values.\n */\n async hmget(key: string, ...fields: string[]): Promise<Array<string | null>> {\n const driver = await this.getDriver();\n return driver.hmget(key, ...fields);\n }\n\n /**\n * Gets all fields and values of hash.\n */\n async hgetall(key: string): Promise<Record<string, string>> {\n const driver = await this.getDriver();\n return driver.hgetall(key);\n }\n\n /**\n * Deletes hash fields.\n */\n async hdel(key: string, ...fields: string[]): Promise<number> {\n const driver = await this.getDriver();\n return driver.hdel(key, ...fields);\n }\n\n /**\n * Checks if hash field exists.\n */\n async hexists(key: string, field: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.hexists(key, field);\n }\n\n /**\n * Gets all field names in hash.\n */\n async hkeys(key: string): Promise<string[]> {\n const driver = await this.getDriver();\n return driver.hkeys(key);\n }\n\n /**\n * Gets all values in hash.\n */\n async hvals(key: string): Promise<string[]> {\n const driver = await this.getDriver();\n return driver.hvals(key);\n }\n\n /**\n * Gets number of fields in hash.\n */\n async hlen(key: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.hlen(key);\n }\n\n /**\n * Increments hash field by integer.\n */\n async hincrby(key: string, field: string, increment: number): Promise<number> {\n const driver = await this.getDriver();\n return driver.hincrby(key, field, increment);\n }\n\n /**\n * Scans hash fields.\n */\n async hscan(key: string, cursor: number, options?: IScanOptions): Promise<[string, string[]]> {\n const driver = await this.getDriver();\n return driver.hscan(key, cursor, options);\n }\n\n /**\n * Pushes values to left of list.\n */\n async lpush(key: string, ...values: string[]): Promise<number> {\n const driver = await this.getDriver();\n return driver.lpush(key, ...values);\n }\n\n /**\n * Pushes values to right of list.\n */\n async rpush(key: string, ...values: string[]): Promise<number> {\n const driver = await this.getDriver();\n return driver.rpush(key, ...values);\n }\n\n /**\n * Pops value from left of list.\n */\n async lpop(key: string): Promise<string | null> {\n const driver = await this.getDriver();\n return driver.lpop(key);\n }\n\n /**\n * Pops value from right of list.\n */\n async rpop(key: string): Promise<string | null> {\n const driver = await this.getDriver();\n return driver.rpop(key);\n }\n\n /**\n * Gets list length.\n */\n async llen(key: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.llen(key);\n }\n\n /**\n * Gets list range.\n */\n async lrange(key: string, start: number, stop: number): Promise<string[]> {\n const driver = await this.getDriver();\n return driver.lrange(key, start, stop);\n }\n\n /**\n * Trims list to specified range.\n */\n async ltrim(key: string, start: number, stop: number): Promise<'OK'> {\n const driver = await this.getDriver();\n return driver.ltrim(key, start, stop);\n }\n\n /**\n * Gets element at index.\n */\n async lindex(key: string, index: number): Promise<string | null> {\n const driver = await this.getDriver();\n return driver.lindex(key, index);\n }\n\n /**\n * Sets element at index.\n */\n async lset(key: string, index: number, value: string): Promise<'OK'> {\n const driver = await this.getDriver();\n return driver.lset(key, index, value);\n }\n\n /**\n * Adds members to set.\n */\n async sadd(key: string, ...members: string[]): Promise<number> {\n const driver = await this.getDriver();\n return driver.sadd(key, ...members);\n }\n\n /**\n * Removes members from set.\n */\n async srem(key: string, ...members: string[]): Promise<number> {\n const driver = await this.getDriver();\n return driver.srem(key, ...members);\n }\n\n /**\n * Gets all members of set.\n */\n async smembers(key: string): Promise<string[]> {\n const driver = await this.getDriver();\n return driver.smembers(key);\n }\n\n /**\n * Checks if member is in set.\n */\n async sismember(key: string, member: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.sismember(key, member);\n }\n\n /**\n * Gets number of members in set.\n */\n async scard(key: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.scard(key);\n }\n\n /**\n * Gets random member from set.\n */\n async srandmember(key: string, count?: number): Promise<string | string[] | null> {\n const driver = await this.getDriver();\n return driver.srandmember(key, count);\n }\n\n /**\n * Pops random member from set.\n */\n async spop(key: string, count?: number): Promise<string | string[] | null> {\n const driver = await this.getDriver();\n return driver.spop(key, count);\n }\n\n /**\n * Scans set members.\n */\n async sscan(key: string, cursor: number, options?: IScanOptions): Promise<[string, string[]]> {\n const driver = await this.getDriver();\n return driver.sscan(key, cursor, options);\n }\n\n /**\n * Adds members to sorted set with scores.\n */\n async zadd(key: string, ...args: Array<number | string>): Promise<number> {\n const driver = await this.getDriver();\n return driver.zadd(key, ...args);\n }\n\n /**\n * Removes members from sorted set.\n */\n async zrem(key: string, ...members: string[]): Promise<number> {\n const driver = await this.getDriver();\n return driver.zrem(key, ...members);\n }\n\n /**\n * Gets members in range by index.\n */\n async zrange(key: string, start: number, stop: number, withScores?: boolean): Promise<string[]> {\n const driver = await this.getDriver();\n return driver.zrange(key, start, stop, withScores);\n }\n\n /**\n * Gets members in range by score.\n */\n async zrangebyscore(key: string, min: number | string, max: number | string, withScores?: boolean): Promise<string[]> {\n const driver = await this.getDriver();\n return driver.zrangebyscore(key, min, max, withScores);\n }\n\n /**\n * Gets score of member.\n */\n async zscore(key: string, member: string): Promise<string | null> {\n const driver = await this.getDriver();\n return driver.zscore(key, member);\n }\n\n /**\n * Gets number of members in sorted set.\n */\n async zcard(key: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.zcard(key);\n }\n\n /**\n * Gets rank of member.\n */\n async zrank(key: string, member: string): Promise<number | null> {\n const driver = await this.getDriver();\n return driver.zrank(key, member);\n }\n\n /**\n * Increments score of member.\n */\n async zincrby(key: string, increment: number, member: string): Promise<string> {\n const driver = await this.getDriver();\n return driver.zincrby(key, increment, member);\n }\n\n /**\n * Scans sorted set members.\n */\n async zscan(key: string, cursor: number, options?: IScanOptions): Promise<[string, string[]]> {\n const driver = await this.getDriver();\n return driver.zscan(key, cursor, options);\n }\n\n /**\n * Publishes message to channel.\n */\n async publish(channel: string, message: string): Promise<number> {\n const driver = await this.getDriver();\n return driver.publish(channel, message);\n }\n\n /**\n * Subscribes to channels.\n */\n async subscribe(...channels: string[]): Promise<void> {\n const driver = await this.getDriver();\n return driver.subscribe(...channels);\n }\n\n /**\n * Unsubscribes from channels.\n */\n async unsubscribe(...channels: string[]): Promise<void> {\n const driver = await this.getDriver();\n return driver.unsubscribe(...channels);\n }\n\n /**\n * Subscribes to channels by pattern.\n */\n async psubscribe(...patterns: string[]): Promise<void> {\n const driver = await this.getDriver();\n return driver.psubscribe(...patterns);\n }\n\n /**\n * Unsubscribes from channel patterns.\n */\n async punsubscribe(...patterns: string[]): Promise<void> {\n const driver = await this.getDriver();\n return driver.punsubscribe(...patterns);\n }\n\n /**\n * Creates a pipeline for batching commands.\n */\n async pipeline(): Promise<IPipeline> {\n const driver = await this.getDriver();\n return driver.pipeline();\n }\n\n /**\n * Creates a multi/exec transaction.\n */\n async multi(): Promise<IMulti> {\n const driver = await this.getDriver();\n return driver.multi();\n }\n\n /**\n * Evaluates Lua script.\n */\n async eval(script: string, keys: string[], args: Array<string | number>): Promise<unknown> {\n const driver = await this.getDriver();\n return driver.eval(script, keys, args);\n }\n\n /**\n * Evaluates Lua script by SHA.\n */\n async evalsha(sha: string, keys: string[], args: Array<string | number>): Promise<unknown> {\n const driver = await this.getDriver();\n return driver.evalsha(sha, keys, args);\n }\n\n /**\n * Loads Lua script and returns SHA.\n */\n async scriptLoad(script: string): Promise<string> {\n const driver = await this.getDriver();\n return driver.scriptLoad(script);\n }\n\n /**\n * Checks if scripts exist.\n */\n async scriptExists(...shas: string[]): Promise<number[]> {\n const driver = await this.getDriver();\n return driver.scriptExists(...shas);\n }\n\n /**\n * Flushes all scripts from cache.\n */\n async scriptFlush(): Promise<'OK'> {\n const driver = await this.getDriver();\n return driver.scriptFlush();\n }\n\n /**\n * Flushes all keys from current database.\n */\n async flushdb(): Promise<'OK'> {\n const driver = await this.getDriver();\n return driver.flushdb();\n }\n\n /**\n * Flushes all keys from all databases.\n */\n async flushall(): Promise<'OK'> {\n const driver = await this.getDriver();\n return driver.flushall();\n }\n\n /**\n * Gets server info.\n */\n async info(section?: string): Promise<string> {\n const driver = await this.getDriver();\n return driver.info(section);\n }\n\n /**\n * Gets database size (number of keys).\n */\n async dbsize(): Promise<number> {\n const driver = await this.getDriver();\n return driver.dbsize();\n }\n\n /**\n * NestJS lifecycle hook - cleanup on module destroy.\n */\n async onModuleDestroy(): Promise<void> {\n await this.clientManager.closeAll();\n }\n}\n","import { Injectable, Inject, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';\nimport { ModuleRef } from '@nestjs/core';\n\nimport { RedisClientManager } from '../../client';\nimport { CLIENT_MANAGER, REDIS_MODULE_OPTIONS, REGISTERED_PLUGINS } from '../../shared/constants';\nimport { RedisXError, ErrorCode } from '../../errors';\nimport { IRedisModuleOptions } from '../../types';\nimport { IRedisXPlugin, IPluginContext, IClientManager, IRedisXConfig, IRedisXLogger } from '../domain/interfaces';\n\n/**\n * Manages plugin lifecycle hooks and dependency ordering.\n *\n * Calls `onRegister()` and `onModuleInit()` on all plugins during\n * NestJS module initialization, and `onModuleDestroy()` during shutdown.\n * Plugins with `dependencies` are initialized in topological order.\n */\n@Injectable()\nexport class PluginRegistryService implements OnModuleInit, OnModuleDestroy {\n private readonly logger = new Logger(PluginRegistryService.name);\n\n constructor(\n @Inject(REGISTERED_PLUGINS) private readonly plugins: IRedisXPlugin[],\n @Inject(CLIENT_MANAGER) private readonly clientManager: RedisClientManager,\n @Inject(REDIS_MODULE_OPTIONS) private readonly options: IRedisModuleOptions,\n private readonly moduleRef: ModuleRef,\n ) {}\n\n async onModuleInit(): Promise<void> {\n if (this.plugins.length === 0) {\n return;\n }\n\n const sorted = this.sortByDependencies(this.plugins);\n const context = this.createContext();\n\n for (const plugin of sorted) {\n if (plugin.onRegister) {\n this.logger.debug(`Calling onRegister for plugin \"${plugin.name}\"`);\n await plugin.onRegister(context);\n }\n }\n\n for (const plugin of sorted) {\n if (plugin.onModuleInit) {\n this.logger.debug(`Calling onModuleInit for plugin \"${plugin.name}\"`);\n await plugin.onModuleInit(context);\n }\n }\n }\n\n async onModuleDestroy(): Promise<void> {\n if (this.plugins.length === 0) {\n return;\n }\n\n const sorted = this.sortByDependencies(this.plugins).reverse();\n const context = this.createContext();\n\n for (const plugin of sorted) {\n if (plugin.onModuleDestroy) {\n this.logger.debug(`Calling onModuleDestroy for plugin \"${plugin.name}\"`);\n await plugin.onModuleDestroy(context);\n }\n }\n }\n\n private createContext(): IPluginContext {\n const plugins = this.plugins;\n const clientManager = this.clientManager;\n const moduleRef = this.moduleRef;\n\n const pluginClientManager: IClientManager = {\n getClient(name?: string) {\n return clientManager.getClient(name);\n },\n hasClient(name: string): boolean {\n return clientManager.hasClient(name);\n },\n getClientNames(): string[] {\n return clientManager.getClientNames();\n },\n };\n\n const pluginLogger: IRedisXLogger = {\n debug: (message: string, context?: Record<string, unknown>) => {\n this.logger.debug(message, context ? JSON.stringify(context) : undefined);\n },\n info: (message: string, context?: Record<string, unknown>) => {\n this.logger.log(message, context ? JSON.stringify(context) : undefined);\n },\n warn: (message: string, context?: Record<string, unknown>) => {\n this.logger.warn(message, context ? JSON.stringify(context) : undefined);\n },\n error: (message: string, error?: Error, context?: Record<string, unknown>) => {\n this.logger.error(message, error?.stack, context ? JSON.stringify(context) : undefined);\n },\n };\n\n const pluginConfig: IRedisXConfig = {\n clients: {},\n plugins,\n global: {\n debug: this.options.global?.debug,\n defaultTtl: this.options.global?.defaultTtl,\n keyPrefix: this.options.global?.keyPrefix,\n },\n };\n\n return {\n clientManager: pluginClientManager,\n config: pluginConfig,\n logger: pluginLogger,\n moduleRef,\n getPlugin: <T extends IRedisXPlugin>(name: string): T | undefined => {\n return plugins.find((p) => p.name === name) as T | undefined;\n },\n hasPlugin: (name: string): boolean => {\n return plugins.some((p) => p.name === name);\n },\n };\n }\n\n /**\n * Topological sort of plugins by dependencies (Kahn's algorithm).\n * Plugins without dependencies maintain their original order.\n * Throws on circular dependencies.\n */\n sortByDependencies(plugins: IRedisXPlugin[]): IRedisXPlugin[] {\n if (plugins.length === 0) {\n return [];\n }\n\n const pluginMap = new Map<string, IRedisXPlugin>();\n const inDegree = new Map<string, number>();\n const adjacency = new Map<string, string[]>();\n\n for (const plugin of plugins) {\n pluginMap.set(plugin.name, plugin);\n inDegree.set(plugin.name, 0);\n adjacency.set(plugin.name, []);\n }\n\n // Build the graph: if A depends on B, edge B -> A (B must come before A)\n for (const plugin of plugins) {\n const deps = plugin.dependencies || [];\n for (const dep of deps) {\n if (!pluginMap.has(dep)) {\n throw new RedisXError(`Plugin \"${plugin.name}\" depends on \"${dep}\" which is not registered`, ErrorCode.PLUGIN_DEPENDENCY_MISSING, undefined, { plugin: plugin.name, missingDependency: dep });\n }\n adjacency.get(dep)!.push(plugin.name);\n inDegree.set(plugin.name, (inDegree.get(plugin.name) || 0) + 1);\n }\n }\n\n // Kahn's algorithm — use original order for plugins with same in-degree\n const originalIndex = new Map<string, number>();\n plugins.forEach((p, i) => originalIndex.set(p.name, i));\n\n const queue: string[] = [];\n for (const plugin of plugins) {\n if (inDegree.get(plugin.name) === 0) {\n queue.push(plugin.name);\n }\n }\n // Sort the initial queue by original order\n queue.sort((a, b) => originalIndex.get(a)! - originalIndex.get(b)!);\n\n const sorted: IRedisXPlugin[] = [];\n\n while (queue.length > 0) {\n const name = queue.shift()!;\n sorted.push(pluginMap.get(name)!);\n\n const neighbors = adjacency.get(name) || [];\n const newReady: string[] = [];\n for (const neighbor of neighbors) {\n const deg = inDegree.get(neighbor)! - 1;\n inDegree.set(neighbor, deg);\n if (deg === 0) {\n newReady.push(neighbor);\n }\n }\n // Sort newly ready by original order\n newReady.sort((a, b) => originalIndex.get(a)! - originalIndex.get(b)!);\n queue.push(...newReady);\n }\n\n if (sorted.length !== plugins.length) {\n const remaining = plugins.filter((p) => !sorted.some((s) => s.name === p.name)).map((p) => p.name);\n throw new RedisXError(`Circular dependency detected among plugins: ${remaining.join(', ')}`, ErrorCode.PLUGIN_CIRCULAR_DEPENDENCY, undefined, { circularPlugins: remaining });\n }\n\n return sorted;\n }\n}\n","import { Inject } from '@nestjs/common';\n\nimport { getClientToken, DEFAULT_CLIENT_NAME } from '../../shared/constants';\n\n/**\n * Injects Redis driver by name.\n *\n * Use this decorator to inject a specific Redis client into your service.\n * If no name is provided, the default client is injected.\n *\n * @param name - Client name (default: 'default')\n * @returns Property decorator\n *\n * @example\n * ```typescript\n * @Injectable()\n * export class CacheService {\n * constructor(\n * @InjectRedis() private readonly redis: IRedisDriver,\n * @InjectRedis('sessions') private readonly sessions: IRedisDriver,\n * ) {}\n *\n * async cacheUser(id: string, data: User): Promise<void> {\n * await this.redis.set(`user:${id}`, JSON.stringify(data), { ex: 3600 });\n * }\n *\n * async getSession(id: string): Promise<string | null> {\n * return this.sessions.get(`session:${id}`);\n * }\n * }\n * ```\n */\nexport function InjectRedis(name: string = DEFAULT_CLIENT_NAME): ParameterDecorator {\n return Inject(getClientToken(name));\n}\n"],"mappings":"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","names":["import_common","import_events","import_common","DriverEvent","ErrorCode","EventEmitter","ConnectionStatus","Redis","sentinel","ManagerEvent","EventEmitter","import_common","import_common","providers","exports","import_common"]}