/// <reference types="node" />
import { RedisClientOptions, RedisClientType, WithFunctions, WithModules, WithScripts } from '../client';
import { CommandOptions } from '../client/commands-queue';
import { CommandArguments, CommanderConfig, CommandSignature, TypeMapping, RedisArgument, RedisFunctions, RedisModules, RedisScripts, ReplyUnion, RespVersions } from '../RESP/types';
import { NON_STICKY_COMMANDS } from '../commands';
import { EventEmitter } from 'node:events';
import RedisClusterSlots, { NodeAddressMap, ShardNode } from './cluster-slots';
import { RedisClusterMultiCommandType } from './multi-command';
import { PubSubListener, PubSubListeners } from '../client/pub-sub';
import { RedisTcpSocketOptions } from '../client/socket';
import { ClientSideCacheConfig, PooledClientSideCacheProvider } from '../client/cache';
import { CommandParser } from '../client/parser';
import { ClientIdentity } from '../client/identity';
import { FieldsetRegistry } from '../himport/registry';
export type ClusterTopologyRefreshOnReconnectionAttemptStrategy = false | number | ((firstReconnectionAt: number) => false | number | undefined);
type WithCommands<RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
    [P in keyof typeof NON_STICKY_COMMANDS]: CommandSignature<(typeof NON_STICKY_COMMANDS)[P], RESP, TYPE_MAPPING>;
};
interface ClusterCommander<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> extends CommanderConfig<M, F, S, RESP> {
    commandOptions?: ClusterCommandOptions<TYPE_MAPPING>;
    /**
     * Prefix prepended to every key sent to Redis (ioredis-compatible `keyPrefix`).
     *
     * Applied by the cluster client itself; the slot of each command is computed from the
     * prefixed key, so routing stays correct. It is intentionally a cluster-level option
     * (not a per-node `rootNodes`/`defaults` option) so it is applied exactly once.
     *
     * Matches ioredis semantics: only keys *sent* to Redis are prefixed. Keys *returned*
     * by Redis are NOT un-prefixed, `MATCH` patterns are NOT auto-prefixed, and Pub/Sub
     * channels are NOT prefixed.
     */
    keyPrefix?: RedisArgument;
}
export type RedisClusterClientOptions = Omit<RedisClientOptions<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, RedisTcpSocketOptions>, keyof ClusterCommander<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping>>;
export interface RedisClusterOptions<M extends RedisModules = RedisModules, F extends RedisFunctions = RedisFunctions, S extends RedisScripts = RedisScripts, RESP extends RespVersions = 3, TYPE_MAPPING extends TypeMapping = TypeMapping> extends ClusterCommander<M, F, S, RESP, TYPE_MAPPING> {
    /**
     * Should contain details for some of the cluster nodes that the client will use to discover
     * the "cluster topology". We recommend including details for at least 3 nodes here.
     *
     * Note: this configuration is only used for the connections that discover the topology — it is
     * not inherited by the connections made to the discovered nodes. Settings that should apply to
     * every connection (e.g. credentials, TLS) must be specified via `defaults`.
     */
    rootNodes: Array<RedisClusterClientOptions>;
    /**
     * Default values used for every client in the cluster. Use this to specify global values,
     * for example: ACL credentials, timeouts, TLS configuration etc.
     *
     * The connections to the discovered cluster nodes are created from these defaults (plus the
     * discovered host and port) — they do not inherit any other settings from `rootNodes`.
     */
    defaults?: Partial<RedisClusterClientOptions>;
    /**
     * When `true`, `.connect()` will only discover the cluster topology, without actually connecting to all the nodes.
     * Useful for short-term or PubSub-only connections.
     */
    minimizeConnections?: boolean;
    /**
     * When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes.
     */
    useReplicas?: boolean;
    /**
     * The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors.
     */
    maxCommandRedirections?: number;
    /**
     * The number of milliseconds after the first post-ready node reconnection attempt
     * before background cluster topology refreshes are triggered. Omitted or `undefined`
     * uses the default delay of `5000`.
     * Use `false` or `0` to disable reconnect-triggered topology refreshes. A function can
     * return the delay dynamically, or `false`/`undefined`/`0` to skip the refresh attempt.
     * Concurrent refreshes are de-duplicated.
     */
    topologyRefreshOnReconnectionAttemptStrategy?: ClusterTopologyRefreshOnReconnectionAttemptStrategy;
    /**
     * Mapping between the addresses in the cluster (see `CLUSTER SHARDS`) and the addresses the client should connect to
     * Useful when the cluster is running on another network
     */
    nodeAddressMap?: NodeAddressMap;
    /**
     * Client Side Caching configuration for the pool.
     *
     * Enables Redis Servers and Clients to work together to cache results from commands
     * sent to a server. The server will notify the client when cached results are no longer valid.
     * In pooled mode, the cache is shared across all clients in the pool.
     *
     * Note: Client Side Caching is only supported with RESP3.
     *
     * @example Anonymous cache configuration
     * ```
     * const client = createCluster({
     *   clientSideCache: {
     *     ttl: 0,
     *     maxEntries: 0,
     *     evictPolicy: "LRU"
     *   },
     *   minimum: 5
     * });
     * ```
     *
     * @example Using a controllable cache
     * ```
     * const cache = new BasicPooledClientSideCache({
     *   ttl: 0,
     *   maxEntries: 0,
     *   evictPolicy: "LRU"
     * });
     * const client = createCluster({
     *   clientSideCache: cache,
     *   minimum: 5
     * });
     * ```
     */
    clientSideCache?: PooledClientSideCacheProvider | ClientSideCacheConfig;
    /**
     * @internal
     * Shared HIMPORT fieldset registry for the whole cluster. When omitted the cluster's
     * `RedisClusterSlots` owns a fresh one; `duplicate()` injects the parent's so the duplicate
     * SHARES the parent's registrations (matching the standalone `duplicate()` guarantee).
     * Not a user-facing option.
     */
    himportRegistry?: FieldsetRegistry;
}
export type RedisClusterType<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 3, TYPE_MAPPING extends TypeMapping = {}> = (RedisCluster<M, F, S, RESP, TYPE_MAPPING> & WithCommands<RESP, TYPE_MAPPING> & WithModules<M, RESP, TYPE_MAPPING> & WithFunctions<F, RESP, TYPE_MAPPING> & WithScripts<S, RESP, TYPE_MAPPING>);
export type ClusterCommandOptions<TYPE_MAPPING extends TypeMapping = TypeMapping> = CommandOptions<TYPE_MAPPING>;
export default class RedisCluster<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> extends EventEmitter {
    #private;
    static factory<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 3, TYPE_MAPPING extends TypeMapping = {}>(config?: ClusterCommander<M, F, S, RESP, TYPE_MAPPING>): (options?: Omit<RedisClusterOptions, keyof Exclude<typeof config, undefined>>) => RedisClusterType<M, F, S, RESP, TYPE_MAPPING>;
    /**
     * Creates a new Redis Cluster client.
     *
     * Note: `rootNodes` is only used to discover the cluster topology; its configuration is not
     * inherited by the connections made to the discovered nodes. Any setting that should apply to
     * every connection in the cluster (e.g. credentials, TLS) must be specified via `defaults`:
     *
     * @example
     * ```javascript
     * createCluster({
     *   rootNodes: [{ url: 'rediss://external-host.io:30001' }],
     *   defaults: {
     *     password: 'password',
     *     socket: { tls: true }
     *   }
     * });
     * ```
     */
    static create<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 3, TYPE_MAPPING extends TypeMapping = {}>(options?: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING>): RedisClusterType<M, F, S, RESP, TYPE_MAPPING>;
    readonly _options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING>;
    readonly _slots: RedisClusterSlots<M, F, S, RESP, TYPE_MAPPING>;
    private _self;
    private _commandOptions?;
    private _policyResolver;
    /**
     * An array of the cluster slots, each slot contain its `master` and `replicas`.
     * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
     */
    get slots(): import("./cluster-slots").Shard<M, F, S, RESP, TYPE_MAPPING>[];
    get clientSideCache(): PooledClientSideCacheProvider | undefined;
    /**
     * The configured key prefix (see {@link RedisClusterOptions.keyPrefix}), if any.
     * @internal
     */
    get _keyPrefix(): RedisArgument | undefined;
    /**
     * An array of the cluster masters.
     * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific master node.
     */
    get masters(): import("./cluster-slots").MasterNode<M, F, S, RESP, TYPE_MAPPING>[];
    /**
     * An array of the cluster replicas.
     * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific replica node.
     */
    get replicas(): ShardNode<M, F, S, RESP, TYPE_MAPPING>[];
    /**
     * A map form a node address (`<host>:<port>`) to its shard, each shard contain its `master` and `replicas`.
     * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
     */
    get nodeByAddress(): Map<string, import("./cluster-slots").MasterNode<M, F, S, RESP, TYPE_MAPPING> | ShardNode<M, F, S, RESP, TYPE_MAPPING>>;
    /**
     * The current pub/sub node.
     */
    get pubSubNode(): (Omit<import("./cluster-slots").Node<M, F, S, RESP, TYPE_MAPPING>, "client"> & Required<Pick<import("./cluster-slots").Node<M, F, S, RESP, TYPE_MAPPING>, "client">>) | undefined;
    get isOpen(): boolean;
    get isReady(): boolean;
    /**
     * @internal
     * Returns the cluster identity for tracking in metrics.
     */
    get identity(): ClientIdentity;
    constructor(options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING>);
    duplicate<_M extends RedisModules = M, _F extends RedisFunctions = F, _S extends RedisScripts = S, _RESP extends RespVersions = RESP, _TYPE_MAPPING extends TypeMapping = TYPE_MAPPING>(overrides?: Partial<RedisClusterOptions<_M, _F, _S, _RESP, _TYPE_MAPPING>>): RedisClusterType<_M, _F, _S, _RESP, _TYPE_MAPPING>;
    connect(): Promise<RedisClusterType<M, F, S, RESP, TYPE_MAPPING>>;
    withCommandOptions<OPTIONS extends ClusterCommandOptions<TYPE_MAPPING>, TYPE_MAPPING extends TypeMapping>(options: OPTIONS): RedisClusterType<M, F, S, RESP, TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {}>;
    private _commandOptionsProxy;
    /**
     * Override the `typeMapping` command option
     */
    withTypeMapping<TYPE_MAPPING extends TypeMapping>(typeMapping: TYPE_MAPPING): RedisClusterType<M, F, S, RESP, TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {}>;
    _handleAsk<T>(fn: (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, opts?: ClusterCommandOptions) => Promise<T>): (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, options?: ClusterCommandOptions) => Promise<T>;
    /**
     * Resolves the command's policies and executes it accordingly: the request
     * policy picks the target clients, each target runs through the core
     * `_execute` transport primitive, and the response policy aggregates the
     * replies. Call sites pass a `makeFn` factory that builds the per-client
     * execution closure (command, script, or raw `sendCommand`).
     */
    _executeWithPolicies<T>(parser: CommandParser, isReadonly: boolean | undefined, options: ClusterCommandOptions | undefined, makeFn: (parser: CommandParser) => (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, opts?: ClusterCommandOptions) => Promise<T>): Promise<T>;
    /**
     * Core transport primitive: sends one command to one client — resolved by
     * the parser's first key unless `pinnedClient` is given — with MOVED/ASK
     * redirect handling. Policy-free; fan-out and aggregation live in
     * `_executeWithPolicies`.
     */
    _execute<T>(parser: CommandParser, isReadonly: boolean | undefined, options: ClusterCommandOptions | undefined, fn: (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, opts?: ClusterCommandOptions) => Promise<T>, pinnedClient?: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, served?: {
        client?: RedisClientType<M, F, S, RESP, TYPE_MAPPING>;
    }): Promise<T>;
    sendCommand<T = ReplyUnion>(firstKey: RedisArgument | undefined, isReadonly: boolean | undefined, args: CommandArguments, options?: ClusterCommandOptions): Promise<T>;
    MULTI(routing?: RedisArgument): RedisClusterMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>;
    multi: (routing?: RedisArgument) => RedisClusterMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>;
    SUBSCRIBE<T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T): Promise<void>;
    subscribe: <T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T | undefined) => Promise<void>;
    UNSUBSCRIBE<T extends boolean = false>(channels?: string | Array<string>, listener?: PubSubListener<boolean>, bufferMode?: T): Promise<void>;
    unsubscribe: <T extends boolean = false>(channels?: string | Array<string>, listener?: PubSubListener<boolean>, bufferMode?: T | undefined) => Promise<void>;
    PSUBSCRIBE<T extends boolean = false>(patterns: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T): Promise<void>;
    pSubscribe: <T extends boolean = false>(patterns: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T | undefined) => Promise<void>;
    PUNSUBSCRIBE<T extends boolean = false>(patterns?: string | Array<string>, listener?: PubSubListener<T>, bufferMode?: T): Promise<void>;
    pUnsubscribe: <T extends boolean = false>(patterns?: string | Array<string>, listener?: PubSubListener<T> | undefined, bufferMode?: T | undefined) => Promise<void>;
    SSUBSCRIBE<T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T): Promise<void>;
    sSubscribe: <T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T | undefined) => Promise<void>;
    SUNSUBSCRIBE<T extends boolean = false>(channels: string | Array<string>, listener?: PubSubListener<T>, bufferMode?: T): Promise<void>;
    resubscribeAllPubSubListeners(allListeners: Partial<PubSubListeners>): void;
    sUnsubscribe: <T extends boolean = false>(channels: string | Array<string>, listener?: PubSubListener<T> | undefined, bufferMode?: T | undefined) => Promise<void>;
    /**
     * @deprecated Use `close` instead.
     */
    quit(): Promise<void>;
    /**
     * @deprecated Use `destroy` instead.
     */
    disconnect(): Promise<void>;
    close(): Promise<void>;
    destroy(): void;
    nodeClient(node: ShardNode<M, F, S, RESP, TYPE_MAPPING>): Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>>;
    /**
     * Returns a random node from the cluster.
     * Userful for running "forward" commands (like PUBLISH) on a random node.
     */
    getRandomNode(): ShardNode<M, F, S, RESP, TYPE_MAPPING>;
    /**
     * Get a random node from a slot.
     * Useful for running readonly commands on a slot.
     */
    getSlotRandomNode(slot: number): ShardNode<M, F, S, RESP, TYPE_MAPPING>;
    /**
     * Returns the connected node client responsible for the given key's slot.
     * Useful for connection-level operations that the cluster client does not expose
     * directly, such as `WATCH` followed by `MULTI`/`EXEC`:
     *
     * ```javascript
     * const nodeClient = await cluster.getNodeClientForKey(key);
     * await nodeClient.WATCH(key);
     * const value = await nodeClient.GET(key);
     * const reply = await nodeClient.MULTI()
     *   .SET(key, calculateNewValue(value))
     *   .EXEC(); // `null` if `key` changed, retry
     * ```
     *
     * @param key - The key whose slot determines the node.
     * @param isReadonly - If `true`, may return a replica client; otherwise returns the slot master.
     */
    getNodeClientForKey(key: RedisArgument, isReadonly?: boolean): Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>>;
    /**
     * @deprecated use `.masters` instead
     * TODO
     */
    getMasters(): import("./cluster-slots").MasterNode<M, F, S, RESP, TYPE_MAPPING>[];
    /**
     * @deprecated use `.slots[<SLOT>]` instead
     * TODO
     */
    getSlotMaster(slot: number): import("./cluster-slots").MasterNode<M, F, S, RESP, TYPE_MAPPING>;
}
export {};
//# sourceMappingURL=index.d.ts.map