js-valkey-server 0.3.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,502 @@
1
+ import { R as RedisCluster, a as RedisServerState, C as CompatibilitySpec, L as Logger, b as RedisClusterNodeHandle, c as Resp2Server, d as CommandExecutor, e as RedisValue } from './redis-error-C96iHLJ-.js';
2
+ export { f as CompatibilityProfile, g as CountGreaterThanZeroError, D as DiscardWithoutMultiError, E as ExecWithoutMultiError, h as ExpectedFloatError, i as ExpectedIntegerError, F as FeatureId, H as HashValueNotFloatError, j as HashValueNotIntegerError, I as IndexOutOfRangeError, k as InvalidExpireTimeError, l as LimitCantBeNegativeError, M as MinMaxNotFloatError, N as NoAuthError, m as NoPasswordConfiguredError, n as NoScriptError, o as NoSuchKeyError, p as NumKeysGreaterThanZeroError, O as OffsetOutOfRangeError, P as PositiveCountError, q as RedisClusterDownError, r as RedisClusterOptions, s as RedisCommandError, t as RedisCrossSlotError, u as RedisFlavor, v as RedisMovedError, w as RedisSyntaxError, x as ResultingScoreNaNError, S as ScriptCallNoCommandError, y as ScriptDebugModeError, z as ScriptFlushOptionError, A as ScriptUnknownCommandError, B as StreamElementTooLargeError, G as StreamIdExhaustedError, T as TransactionDiscardedError, U as UnknownClusterSubcommandError, J as UnknownRedisCommandError, K as UnknownScriptSubcommandError, V as VersionGate, W as WatchInsideMultiError, Q as WrongNumberOfArgumentsError, X as WrongNumberOfKeysError, Y as WrongPassError, Z as WrongTypeRedisError, _ as ZaddGtLtNxConflictError, $ as ZaddIncrPairError, a0 as ZaddNxXxConflictError, a1 as buildRedisCluster, a2 as computeSlotRange, a3 as createRedisCluster, a4 as gateSatisfied, a5 as resolveCompatibilityProfile } from './redis-error-C96iHLJ-.js';
3
+ import { Redis, Cluster } from 'ioredis';
4
+ import 'net';
5
+ import 'lua-redis-wasm';
6
+
7
+ /**
8
+ * Public seeding contract for {@link RedisMock}. Callers describe data with
9
+ * plain keys/types/values (plus optional `ttlMs` and `db`); the mock owns the
10
+ * conversion into the internal {@link RedisDataValue} representation and the
11
+ * placement onto the right database (and, in cluster mode, the right node).
12
+ *
13
+ * Streams are intentionally not seedable yet — their public entry shape (entry
14
+ * ids, fields, consumer groups) is not finalized, and exposing the internal
15
+ * stream state would leak implementation details.
16
+ */
17
+ type SeedEntry = {
18
+ key: string;
19
+ type: 'string';
20
+ value: string | number;
21
+ ttlMs?: number;
22
+ db?: number;
23
+ } | {
24
+ key: string;
25
+ type: 'hash';
26
+ value: Record<string, string | number>;
27
+ ttlMs?: number;
28
+ db?: number;
29
+ } | {
30
+ key: string;
31
+ type: 'list';
32
+ value: (string | number)[];
33
+ ttlMs?: number;
34
+ db?: number;
35
+ } | {
36
+ key: string;
37
+ type: 'set';
38
+ value: (string | number)[];
39
+ ttlMs?: number;
40
+ db?: number;
41
+ } | {
42
+ key: string;
43
+ type: 'zset';
44
+ value: Record<string, number>;
45
+ ttlMs?: number;
46
+ db?: number;
47
+ };
48
+ declare function seedStandalone(state: RedisServerState, entries: readonly SeedEntry[]): Promise<void>;
49
+ /**
50
+ * The minimal cluster shape `seedCluster` needs: a topology to slot keys and the
51
+ * per-node `RedisServerState` to write into. Both the socket-backed
52
+ * {@link RedisCluster} (`.nodes[].server`) and the TCP-free `buildClusterNodes`
53
+ * product satisfy this structurally, so the in-memory ioredis mock can reuse the
54
+ * same seeder.
55
+ */
56
+ type SeedClusterTarget = {
57
+ topology: RedisCluster['topology'];
58
+ nodes: readonly {
59
+ id: string;
60
+ server: RedisServerState;
61
+ }[];
62
+ };
63
+ declare function seedCluster(cluster: SeedClusterTarget, entries: readonly SeedEntry[]): Promise<void>;
64
+
65
+ type RedisAddress = {
66
+ host: string;
67
+ port: number;
68
+ };
69
+ type CreateRedisServerOptions = {
70
+ /** Port to bind. Defaults to `0` (OS-assigned free port). */
71
+ port?: number;
72
+ /** Logical database count. Defaults to 16, matching real Redis. */
73
+ databaseCount?: number;
74
+ compatibility?: CompatibilitySpec;
75
+ logger?: Pick<Logger, 'error'>;
76
+ };
77
+ type CreateRedisServerClusterOptions = {
78
+ /** Build a (listening) cluster instead of a standalone server. */
79
+ cluster: RedisMockClusterOptions;
80
+ /** Cluster base port. Defaults to `0` (each node OS-assigned). */
81
+ basePort?: number;
82
+ /** Databases per cluster node. Defaults to 1. */
83
+ databasesPerNode?: number;
84
+ compatibility?: CompatibilitySpec;
85
+ logger?: Pick<Logger, 'error'>;
86
+ };
87
+ type RedisServerHandle = {
88
+ host: string;
89
+ port: number;
90
+ state: RedisServerState;
91
+ server: Resp2Server;
92
+ close(): Promise<void>;
93
+ };
94
+ /**
95
+ * Runs a real, **listening** Redis server you connect a normal client library
96
+ * to. Standalone by default (one {@link RedisServerState} + executor +
97
+ * {@link Resp2Server}, 16 databases); pass `cluster` to build and start a whole
98
+ * cluster instead — the returned {@link RedisCluster} is already listening.
99
+ *
100
+ * For tests prefer {@link createRedisMock}, which wraps this and adds seeding,
101
+ * reset-between-tests, and an in-process socketless client. Drop to
102
+ * `js-redis-server/core` only when you need to assemble the pipeline by hand.
103
+ *
104
+ * @see {@link createRedisMock} — the test-mock facade (use this for test suites)
105
+ */
106
+ declare function createRedisServer(options?: CreateRedisServerOptions): Promise<RedisServerHandle>;
107
+ declare function createRedisServer(options: CreateRedisServerClusterOptions): Promise<RedisCluster>;
108
+ type RedisMockClusterOptions = {
109
+ masters: number;
110
+ replicas?: number;
111
+ };
112
+ type CreateRedisMockOptions = {
113
+ /** When set, builds a cluster mock instead of a standalone one. */
114
+ cluster?: RedisMockClusterOptions;
115
+ /** Standalone-only: logical database count (defaults to 16). */
116
+ databaseCount?: number;
117
+ /** Standalone bind port. Defaults to `0` (OS-assigned). */
118
+ port?: number;
119
+ /** Cluster base port. Defaults to `0` (each node OS-assigned). */
120
+ basePort?: number;
121
+ compatibility?: CompatibilitySpec;
122
+ logger?: Pick<Logger, 'error'>;
123
+ };
124
+ /**
125
+ * Friendly test-mock facade over a standalone server or a cluster. Exposes
126
+ * connection helpers, seeding, reset-between-tests, and escape hatches to the
127
+ * underlying state/nodes for power users.
128
+ *
129
+ * Need a socketless in-process client (no TCP, no RESP)? Use the standalone
130
+ * {@link createInMemoryClient} builder instead.
131
+ */
132
+ interface RedisMock {
133
+ readonly host: string;
134
+ readonly port: number;
135
+ readonly url: string;
136
+ /**
137
+ * Node addresses as `{ host, port }[]` — a single entry for standalone mocks,
138
+ * every node for cluster mocks. Client-agnostic: index `[0]` for a single
139
+ * client, or pass the whole array to a cluster client.
140
+ *
141
+ * @example new Redis(mock.addresses()[0]) // ioredis, standalone
142
+ * @example new Redis.Cluster(mock.addresses()) // ioredis, cluster
143
+ */
144
+ addresses(): RedisAddress[];
145
+ seed(entries: readonly SeedEntry[]): Promise<void>;
146
+ flush(): Promise<void>;
147
+ /** Alias for {@link RedisMock.flush}. */
148
+ reset(): Promise<void>;
149
+ close(): Promise<void>;
150
+ /** Escape hatch: the underlying state (standalone mocks only). */
151
+ readonly state?: RedisServerState;
152
+ /** Escape hatch: the underlying node handles (cluster mocks only). */
153
+ readonly nodes?: readonly RedisClusterNodeHandle[];
154
+ }
155
+ /**
156
+ * Creates a {@link RedisMock} — the entry point for test suites. Standalone by
157
+ * default; pass `{ cluster: { masters } }` for a cluster mock. Adds seeding and
158
+ * reset-between-tests on top of a real server/cluster.
159
+ *
160
+ * To run a long-running server a separate process connects to (a CLI, a dev
161
+ * tool) rather than drive it from test code, use {@link createRedisServer}
162
+ * instead (it takes the same `cluster` option).
163
+ *
164
+ * @see {@link createRedisServer} — run a real server/cluster without test helpers
165
+ */
166
+ declare function createRedisMock(options?: CreateRedisMockOptions): Promise<RedisMock>;
167
+ /** Alternative name for the same factory; compatibility defaults are unchanged. */
168
+ declare const createValkeyMock: typeof createRedisMock;
169
+
170
+ type CreateIoredisMockOptions = {
171
+ cluster?: false;
172
+ databaseCount?: number;
173
+ seed?: readonly SeedEntry[];
174
+ } | {
175
+ cluster: {
176
+ masters: number;
177
+ replicasPerMaster?: number;
178
+ };
179
+ seed?: readonly SeedEntry[];
180
+ };
181
+ /**
182
+ * A drop-in, socketless `ioredis` client (standalone or `Cluster`) backed by the
183
+ * in-memory server pipeline. The **real** ioredis client drives a fake
184
+ * `net.Socket` ({@link createVirtualConnection}) over real RESP, so reply
185
+ * shaping, pipelines, `multi`, pub/sub, and `scanStream` all work for free — no
186
+ * TCP socket and no port bind.
187
+ *
188
+ * `ioredis` is an optional peer dependency, imported lazily, so the core stays
189
+ * dependency-free.
190
+ */
191
+ declare function createIoredisMock(options?: CreateIoredisMockOptions): Promise<Redis | Cluster>;
192
+
193
+ /** A command argument the in-memory client accepts. */
194
+ type RedisCommandArgument = string | number | Buffer;
195
+ /** Native JS value an in-memory reply decodes to. */
196
+ type RedisNativeReply = string | number | bigint | boolean | Buffer | null | RedisNativeReply[] | {
197
+ [key: string]: RedisNativeReply;
198
+ };
199
+ type InMemoryRedisClientOptions = {
200
+ server: RedisServerState;
201
+ executor: CommandExecutor;
202
+ /** Initial selected database (default 0). */
203
+ database?: number;
204
+ /** Return `Buffer`s for bulk-string/verbatim replies instead of utf8 strings. */
205
+ returnBuffers?: boolean;
206
+ /** Called once when the client is closed — used to tear down owned state. */
207
+ onClose?: () => void;
208
+ };
209
+ /**
210
+ * Socketless, high-level client that drives the **same** command pipeline as a
211
+ * networked client through an in-process {@link ClientSession} — bypassing both
212
+ * the TCP loopback and RESP encoding/decoding. Intended for tests that just need
213
+ * to issue commands and read native JS replies without pulling in a real client
214
+ * library.
215
+ *
216
+ * Streaming commands (SUBSCRIBE / PSUBSCRIBE / MONITOR) are not supported here;
217
+ * use a real client for those.
218
+ */
219
+ declare class InMemoryRedisClient {
220
+ private readonly session;
221
+ private readonly returnBuffers;
222
+ private readonly onClose?;
223
+ private closed;
224
+ /** Aborted on close — tears down any active stream and push readers. */
225
+ private readonly lifetime;
226
+ /**
227
+ * Set once a streaming command (e.g. MONITOR) hands back a `ResponseStream`.
228
+ * `command()` consumes its first frame as the immediate reply; {@link pushes}
229
+ * drains the rest. Pub/sub doesn't set this — its messages flow through the
230
+ * session push channel instead (see {@link pushes}).
231
+ */
232
+ private activeStream?;
233
+ private streamFrames?;
234
+ constructor(options: InMemoryRedisClientOptions);
235
+ /**
236
+ * Run a single command (e.g. `client.command('SET', 'k', 'v')`) and resolve
237
+ * to its native reply. Throws a {@link RedisCommandError} for `-ERR` replies,
238
+ * mirroring what a real client surfaces.
239
+ *
240
+ * Streaming commands (MONITOR / SUBSCRIBE / …) resolve to their *immediate*
241
+ * reply (MONITOR's `OK`, the subscribe confirmation); their server-initiated
242
+ * frames are delivered through {@link pushes}.
243
+ */
244
+ command(name: string, ...args: RedisCommandArgument[]): Promise<RedisNativeReply>;
245
+ /** Alias for {@link InMemoryRedisClient.command}. */
246
+ send(name: string, ...args: RedisCommandArgument[]): Promise<RedisNativeReply>;
247
+ /**
248
+ * True once the last command put the connection into *push mode* — an active
249
+ * MONITOR stream, or a SUBSCRIBE/PSUBSCRIBE/SSUBSCRIBE that entered subscribed
250
+ * mode. Consumers should switch to draining {@link pushes} while this holds.
251
+ */
252
+ get streaming(): boolean;
253
+ /**
254
+ * Server-initiated frames for a connection in *push mode* — pub/sub messages
255
+ * (via the session push channel) and the tail of a MONITOR stream — decoded to
256
+ * native replies. Iterate it after issuing SUBSCRIBE/PSUBSCRIBE/MONITOR. Ends
257
+ * when `signal` (or the connection) is closed.
258
+ */
259
+ pushes(signal?: AbortSignal): AsyncIterable<RedisNativeReply>;
260
+ close(): void;
261
+ private decode;
262
+ }
263
+ type CreateInMemoryRedisOptions = {
264
+ /** Logical database count (defaults to 16, matching real Redis). */
265
+ databaseCount?: number;
266
+ /** Pre-populate the keyspace before any connection is opened. */
267
+ seed?: readonly SeedEntry[];
268
+ /** Redis/Valkey version to emulate (defaults to `redis-8.0`). */
269
+ compatibility?: CompatibilitySpec;
270
+ };
271
+ /** Per-connection options for {@link InMemoryRedis.connect}. */
272
+ type ConnectOptions = {
273
+ /** Initial selected database (default 0). */
274
+ database?: number;
275
+ /** Return `Buffer`s for bulk-string/verbatim replies instead of utf8 strings. */
276
+ returnBuffers?: boolean;
277
+ };
278
+ /**
279
+ * A socketless in-memory Redis *instance* — one shared keyspace + command
280
+ * pipeline that many {@link InMemoryRedisClient} connections can drive at once.
281
+ * Because connections share the underlying {@link RedisServerState}, a `MONITOR`
282
+ * / `SUBSCRIBE` on one connection observes commands run on another, and a
283
+ * `BLPOP` blocks until another connection writes the key — exactly like real
284
+ * Redis. `close()` tears the whole instance down.
285
+ */
286
+ declare class InMemoryRedis {
287
+ private readonly state;
288
+ private readonly executor;
289
+ constructor(state: RedisServerState, executor: CommandExecutor);
290
+ /** Open a new connection (its own {@link ClientSession}) over this keyspace. */
291
+ connect(options?: ConnectOptions): InMemoryRedisClient;
292
+ close(): void;
293
+ }
294
+ /**
295
+ * Build a socketless {@link InMemoryRedis} instance with its own keyspace +
296
+ * command pipeline. Open one or more connections with {@link InMemoryRedis.connect}.
297
+ */
298
+ declare function createInMemoryRedis(options?: CreateInMemoryRedisOptions): Promise<InMemoryRedis>;
299
+ type CreateInMemoryClientOptions = CreateInMemoryRedisOptions & ConnectOptions;
300
+ /**
301
+ * Convenience wrapper: an {@link InMemoryRedis} instance with a single owned
302
+ * connection. `client.close()` tears the whole instance down. For multiple
303
+ * connections over one keyspace (pub/sub, MONITOR, cross-connection blocking),
304
+ * use {@link createInMemoryRedis} and call `connect()` per connection.
305
+ */
306
+ declare function createInMemoryClient(options?: CreateInMemoryClientOptions): Promise<InMemoryRedisClient>;
307
+
308
+ /** A command argument node-redis accepts on the wire. */
309
+ type NodeRedisCommandArgument = string | Buffer;
310
+ /** Native JS value a reply decodes to (mirrors node-redis RESP2 defaults). */
311
+ type NodeRedisReply = string | number | bigint | boolean | Buffer | null | NodeRedisReply[] | {
312
+ [key: string]: NodeRedisReply;
313
+ };
314
+ type NodeRedisMockClusterOptions = {
315
+ masters: number;
316
+ replicas?: number;
317
+ };
318
+ type CreateNodeRedisMockOptions = {
319
+ cluster?: undefined;
320
+ databaseCount?: number;
321
+ } | {
322
+ cluster: NodeRedisMockClusterOptions;
323
+ };
324
+ /** A single `{ score, value }` member for ZADD-style methods. */
325
+ type NodeRedisZMember = {
326
+ score: number;
327
+ value: string;
328
+ };
329
+ /** Listener invoked for each delivered pub/sub message: `(message, channel)`. */
330
+ type NodeRedisPubSubListener = (message: string, channel: string) => void;
331
+ /**
332
+ * Creates an in-memory node-redis-shaped client (standalone) or cluster client.
333
+ * Pass `{ cluster: { masters } }` for a {@link NodeRedisMockCluster}; otherwise
334
+ * a {@link NodeRedisMockClient} backed by a single in-memory pipeline.
335
+ */
336
+ declare function createNodeRedisMock(options?: CreateNodeRedisMockOptions): Promise<NodeRedisMockClient | NodeRedisMockCluster>;
337
+ type FacadeBackend = {
338
+ state: RedisServerState;
339
+ executor: CommandExecutor;
340
+ };
341
+ /**
342
+ * Shared command-method surface implemented by both the standalone client and
343
+ * the cluster client. Standalone runs every command on its own session; the
344
+ * cluster routes by slot to the owning node's session — but the curated method
345
+ * bodies are identical, so they live in this base and dispatch through the
346
+ * abstract {@link CommandRunner.run}.
347
+ */
348
+ declare abstract class CommandRunner {
349
+ /**
350
+ * Execute one already-tokenised command and return its decoded reply.
351
+ * Implementations pick the session (standalone: the only one; cluster: the
352
+ * slot owner for the command's keys).
353
+ */
354
+ protected abstract run(args: NodeRedisCommandArgument[]): Promise<RedisValue>;
355
+ /** Generic escape hatch for any command, decoded to a native JS reply. */
356
+ sendCommand(args: NodeRedisCommandArgument[]): Promise<NodeRedisReply>;
357
+ get(key: string): Promise<string | null>;
358
+ set(key: string, value: string | number, ...rest: NodeRedisCommandArgument[]): Promise<string | null>;
359
+ del(...keys: string[]): Promise<number>;
360
+ exists(...keys: string[]): Promise<number>;
361
+ incr(key: string): Promise<number>;
362
+ expire(key: string, seconds: number): Promise<number>;
363
+ ttl(key: string): Promise<number>;
364
+ hSet(key: string, field: string, value: string): Promise<number>;
365
+ hGet(key: string, field: string): Promise<string | null>;
366
+ hGetAll(key: string): Promise<{
367
+ [field: string]: string;
368
+ }>;
369
+ lPush(key: string, ...values: string[]): Promise<number>;
370
+ rPush(key: string, ...values: string[]): Promise<number>;
371
+ lRange(key: string, start: number, stop: number): Promise<string[]>;
372
+ sAdd(key: string, ...members: string[]): Promise<number>;
373
+ sMembers(key: string): Promise<string[]>;
374
+ zAdd(key: string, members: NodeRedisZMember | NodeRedisZMember[]): Promise<number>;
375
+ zRange(key: string, start: number, stop: number): Promise<string[]>;
376
+ }
377
+ type NodeRedisMockClientInit = FacadeBackend & {
378
+ database?: number;
379
+ /** True only for the client that created the state — it owns its teardown. */
380
+ ownsState?: boolean;
381
+ };
382
+ /**
383
+ * Standalone in-memory node-redis facade. Drives one {@link ClientSession}
384
+ * against a single keyspace; pub/sub uses a *dedicated* session that drains
385
+ * pushes off {@link ClientSession.readPushes}, mirroring node-redis' rule that a
386
+ * subscribed connection is reserved for pub/sub.
387
+ */
388
+ declare class NodeRedisMockClient extends CommandRunner {
389
+ private readonly emitter;
390
+ private readonly backend;
391
+ private readonly database?;
392
+ private readonly ownsState;
393
+ private readonly session;
394
+ /**
395
+ * Serializes commands on this client the way a real single node-redis
396
+ * connection does, so a concurrent (un-awaited) call cannot interleave between
397
+ * the MULTI and EXEC of a transaction sharing {@link session}.
398
+ */
399
+ private commandLock;
400
+ /** Dedicated session + push-reader loop, created lazily on first subscribe. */
401
+ private pubsub?;
402
+ private closed;
403
+ constructor(init: NodeRedisMockClientInit);
404
+ on(event: string, listener: (...args: unknown[]) => void): this;
405
+ once(event: string, listener: (...args: unknown[]) => void): this;
406
+ off(event: string, listener: (...args: unknown[]) => void): this;
407
+ /** node-redis clients require an explicit connect(); here it is a no-op. */
408
+ connect(): Promise<this>;
409
+ /** A fresh, independent client over the **same** shared keyspace. */
410
+ duplicate(): Promise<NodeRedisMockClient>;
411
+ protected run(args: NodeRedisCommandArgument[]): Promise<RedisValue>;
412
+ /** Run `fn` after any in-flight command/transaction on this client settles. */
413
+ private runExclusive;
414
+ publish(channel: string, message: string): Promise<number>;
415
+ subscribe(channel: string, listener: NodeRedisPubSubListener): Promise<void>;
416
+ pSubscribe(pattern: string, listener: NodeRedisPubSubListener): Promise<void>;
417
+ unsubscribe(channel?: string): Promise<void>;
418
+ pUnsubscribe(pattern?: string): Promise<void>;
419
+ watch(...keys: string[]): Promise<string>;
420
+ unwatch(): Promise<string>;
421
+ /** Begin a MULTI transaction. Commands are queued, then replayed on exec(). */
422
+ multi(): NodeRedisMockMulti;
423
+ /** Replay MULTI → queued commands → EXEC on the shared session as one span. */
424
+ private runTransactionSpan;
425
+ /** Gracefully close: tear down pub/sub + the command session. */
426
+ quit(): Promise<string>;
427
+ /** Hard close (node-redis `disconnect()`). Same teardown as quit(). */
428
+ disconnect(): Promise<void>;
429
+ /**
430
+ * node-redis exposes a synchronous destroy(); abort everything immediately.
431
+ * The push-drain loop settles on the next tick via the abort signal.
432
+ */
433
+ destroy(): void;
434
+ private teardown;
435
+ private ensurePubSub;
436
+ private drainPushes;
437
+ private dispatchPush;
438
+ }
439
+ /**
440
+ * MULTI builder mirroring node-redis' chainable transaction API. Queues curated
441
+ * commands and replays them with a real MULTI/EXEC on the owning session, so the
442
+ * server's transaction + WATCH semantics drive the result (EXEC returns an array
443
+ * of replies, or `null` when a watched key changed).
444
+ */
445
+ declare class NodeRedisMockMulti {
446
+ private readonly runTransaction;
447
+ private readonly queued;
448
+ private settled;
449
+ constructor(runTransaction: (queued: NodeRedisCommandArgument[][]) => Promise<RedisValue>);
450
+ set(key: string, value: string | number): this;
451
+ get(key: string): this;
452
+ del(...keys: string[]): this;
453
+ incr(key: string): this;
454
+ hSet(key: string, field: string, value: string): this;
455
+ hGet(key: string, field: string): this;
456
+ /** Generic escape hatch: queue any raw command. */
457
+ addCommand(args: NodeRedisCommandArgument[]): this;
458
+ private queue;
459
+ /**
460
+ * Replay the queued commands inside a real MULTI/EXEC and return the array of
461
+ * decoded replies. Matching node-redis, a watch-aborted transaction throws a
462
+ * `WatchError` (never returns null), and per-command errors are aggregated
463
+ * into a single `MultiErrorReply` carrying every reply + the error indexes.
464
+ */
465
+ exec(): Promise<NodeRedisReply[]>;
466
+ /** Cancel the transaction without running the queued commands. */
467
+ discard(): Promise<void>;
468
+ private assertOpen;
469
+ }
470
+ /**
471
+ * In-memory node-redis cluster facade. Reuses {@link buildClusterNodes} for a
472
+ * TCP-free cluster, then routes each command to the slot owner's session
473
+ * (computed via {@link RedisClusterTopology.calculateSlotForKeys}). The curated
474
+ * method surface is inherited unchanged from {@link CommandRunner}.
475
+ */
476
+ declare class NodeRedisMockCluster extends CommandRunner {
477
+ private readonly emitter;
478
+ private readonly topology;
479
+ private readonly masters;
480
+ private readonly sessions;
481
+ private readonly replicationLinks;
482
+ private closed;
483
+ private constructor();
484
+ static create(options: NodeRedisMockClusterOptions): NodeRedisMockCluster;
485
+ on(event: string, listener: (...args: unknown[]) => void): this;
486
+ once(event: string, listener: (...args: unknown[]) => void): this;
487
+ off(event: string, listener: (...args: unknown[]) => void): this;
488
+ connect(): Promise<this>;
489
+ protected run(args: NodeRedisCommandArgument[]): Promise<RedisValue>;
490
+ quit(): Promise<string>;
491
+ disconnect(): Promise<void>;
492
+ destroy(): void;
493
+ private teardown;
494
+ /**
495
+ * Resolve (and cache) a session on the master that owns the slot for the
496
+ * command's keys. Keyless commands run on the first master.
497
+ */
498
+ private sessionForCommand;
499
+ private sessionFor;
500
+ }
501
+
502
+ export { CompatibilitySpec, type ConnectOptions, type CreateInMemoryClientOptions, type CreateInMemoryRedisOptions, type CreateIoredisMockOptions, type CreateNodeRedisMockOptions, type CreateRedisMockOptions, type CreateRedisServerClusterOptions, type CreateRedisServerOptions, InMemoryRedis, InMemoryRedisClient, type InMemoryRedisClientOptions, Logger, type NodeRedisCommandArgument, NodeRedisMockClient, NodeRedisMockCluster, type NodeRedisMockClusterOptions, NodeRedisMockMulti, type NodeRedisPubSubListener, type NodeRedisReply, type NodeRedisZMember, type RedisAddress, RedisCluster, RedisClusterNodeHandle, type RedisCommandArgument, type RedisMock, type RedisMockClusterOptions, type RedisNativeReply, type RedisServerHandle, type SeedEntry, createInMemoryClient, createInMemoryRedis, createIoredisMock, createNodeRedisMock, createRedisMock, createRedisServer, createValkeyMock, seedCluster, seedStandalone };