You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-09 00:22:08 +03:00
update docs, add 6.0.x to the tests matrix, add eslint, npm update, fix some commands, fix some types
Co-authored-by: Simon Prickett <simon@crudworks.org>
This commit is contained in:
12
.eslintrc.json
Normal file
12
.eslintrc.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
]
|
||||||
|
}
|
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [12.x, 14.x, 16.x]
|
node-version: [12.x, 14.x, 16.x]
|
||||||
redis-version: [5.x, 6.x]
|
redis-version: [5.x, 6.0.x, 6.2.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v2.3.4
|
||||||
|
59
README.md
59
README.md
@@ -57,7 +57,7 @@ createClient({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in in the [Wiki](https://github.com/redis/node-redis/wiki/lib.socket#RedisSocketOptions).
|
You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in the [client configuration guide](./docs/client-configuration.md).
|
||||||
|
|
||||||
### Redis Commands
|
### Redis Commands
|
||||||
|
|
||||||
@@ -227,32 +227,34 @@ import { createClient, defineScript } from 'redis';
|
|||||||
})();
|
})();
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cluster
|
### Disconnecting
|
||||||
|
|
||||||
Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a non-clustered client:
|
There are two functions that disconnect a client from the Redis server. In most scenarios you should use `.quit()` to ensure that pending commands are sent to Redis before closing a connection.
|
||||||
|
|
||||||
|
#### `.QUIT()`/`.quit()`
|
||||||
|
|
||||||
|
Gracefully close a client's connection to Redis, by sending the [`QUIT`](https://redis.io/commands/quit) command to the server. Before quitting, the client executes any remaining commands in its queue, and will receive replies from Redis for each of them.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { createCluster } from 'redis';
|
const [ping, get, quit] = await Promise.all([
|
||||||
|
client.ping(),
|
||||||
|
client.get('key'),
|
||||||
|
client.quit()
|
||||||
|
]); // ['PONG', null, 'OK']
|
||||||
|
|
||||||
(async () => {
|
try {
|
||||||
const cluster = createCluster({
|
await client.get('key');
|
||||||
rootNodes: [
|
} catch (err) {
|
||||||
{
|
// ClosedClient Error
|
||||||
url: 'redis://10.0.0.1:30001'
|
}
|
||||||
},
|
```
|
||||||
{
|
|
||||||
url: 'redis://10.0.0.2:30002'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
cluster.on('error', (err) => console.log('Redis Cluster Error', err));
|
#### `.disconnect()`
|
||||||
|
|
||||||
await cluster.connect();
|
Forcibly close a client's connection to Redis immediately. Calling `disconnect` will not send further pending commands to the Redis server, or wait for or parse outstanding responses.
|
||||||
|
|
||||||
await cluster.set('key', 'value');
|
```typescript
|
||||||
const value = await cluster.get('key');
|
await client.disconnect();
|
||||||
})();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Auto-Pipelining
|
### Auto-Pipelining
|
||||||
@@ -273,6 +275,23 @@ await Promise.all([
|
|||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Clustering
|
||||||
|
|
||||||
|
Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to connect to a Redis Cluster.
|
||||||
|
|
||||||
|
## Supported Redis versions
|
||||||
|
|
||||||
|
Node Redis is supported with the following versions of Redis:
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
|---------|--------------------|
|
||||||
|
| 6.2.z | :heavy_check_mark: |
|
||||||
|
| 6.0.z | :heavy_check_mark: |
|
||||||
|
| 5.y.z | :heavy_check_mark: |
|
||||||
|
| < 5.0 | :x: |
|
||||||
|
|
||||||
|
> Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
If you'd like to contribute, check out the [contributing guide](CONTRIBUTING.md).
|
If you'd like to contribute, check out the [contributing guide](CONTRIBUTING.md).
|
||||||
|
@@ -5,9 +5,9 @@
|
|||||||
Node Redis is generally backwards compatible with very few exceptions, so we recommend users to always use the latest version to experience stability, performance and security.
|
Node Redis is generally backwards compatible with very few exceptions, so we recommend users to always use the latest version to experience stability, performance and security.
|
||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ------- | ------------------ |
|
|---------|--------------------|
|
||||||
| 4.0.x | :white_check_mark: |
|
| 4.0.z | :heavy_check_mark: |
|
||||||
| 3.1.x | :white_check_mark: |
|
| 3.1.z | :heavy_check_mark: |
|
||||||
| < 3.1 | :x: |
|
| < 3.1 | :x: |
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
54
docs/clustering.md
Normal file
54
docs/clustering.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Clustering
|
||||||
|
|
||||||
|
## Basic Example
|
||||||
|
|
||||||
|
Connecting to a cluster is a bit different. Create the client by specifying some (or all) of the nodes in your cluster and then use it like a regular client instance:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { createCluster } from 'redis';
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const cluster = createCluster({
|
||||||
|
rootNodes: [
|
||||||
|
{
|
||||||
|
url: 'redis://10.0.0.1:30001'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: 'redis://10.0.0.2:30002'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
cluster.on('error', (err) => console.log('Redis Cluster Error', err));
|
||||||
|
|
||||||
|
await cluster.connect();
|
||||||
|
|
||||||
|
await cluster.set('key', 'value');
|
||||||
|
const value = await cluster.get('key');
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
## `createCluster` configuration
|
||||||
|
|
||||||
|
> See the [client configuration](./client-configuration.md) page for the `rootNodes` and `defaults` configuration schemas.
|
||||||
|
|
||||||
|
| Property | Default | Description |
|
||||||
|
|------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| rootNodes | | An array of root nodes that are part of the cluster, which will be used to get the cluster topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster, 3 should be enough to reliably connect and obtain the cluster configuration from the server |
|
||||||
|
| defaults | | The default configuration values for every client in the cluster. Use this for example when specifying an ACL user to connect with |
|
||||||
|
| useReplicas | `false` | When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes |
|
||||||
|
| maxCommandRedirections | `16` | The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors | |
|
||||||
|
|
||||||
|
## Command Routing
|
||||||
|
|
||||||
|
### Commands that operate on Redis Keys
|
||||||
|
|
||||||
|
Commands such as `GET`, `SET`, etc. will be routed by the first key, for instance `MGET 1 2 3` will be routed by the key `1`.
|
||||||
|
|
||||||
|
### [Server Commands][https://redis.io/commands#server]
|
||||||
|
|
||||||
|
Admin commands such as `MEMORY STATS`, `FLUSHALL`, etc. are not attached to the cluster, and should be executed on a specific node using `.getSlot()` or `.getAllMasters()`.
|
||||||
|
|
||||||
|
### "Forwarded Commands"
|
||||||
|
|
||||||
|
Some commands (e.g. `PUBLISH`) are forwarded to other cluster nodes by the Redis server. The client will send these commands to a random node in order to spread the load across the cluster.
|
@@ -6,20 +6,20 @@ import { RedisCommandRawReply } from '../commands';
|
|||||||
export interface QueueCommandOptions {
|
export interface QueueCommandOptions {
|
||||||
asap?: boolean;
|
asap?: boolean;
|
||||||
chainId?: symbol;
|
chainId?: symbol;
|
||||||
signal?: any; // TODO: `AbortSignal` type is incorrect
|
signal?: AbortSignal;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CommandWaitingToBeSent extends CommandWaitingForReply {
|
interface CommandWaitingToBeSent extends CommandWaitingForReply {
|
||||||
args: Array<string | Buffer>;
|
args: Array<string | Buffer>;
|
||||||
chainId?: symbol;
|
chainId?: symbol;
|
||||||
abort?: {
|
abort?: {
|
||||||
signal: any; // TODO: `AbortSignal` type is incorrect
|
signal: AbortSignal;
|
||||||
listener(): void;
|
listener(): void;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CommandWaitingForReply {
|
interface CommandWaitingForReply {
|
||||||
resolve(reply?: any): void;
|
resolve(reply?: unknown): void;
|
||||||
reject(err: Error): void;
|
reject(err: Error): void;
|
||||||
channelsCounter?: number;
|
channelsCounter?: number;
|
||||||
bufferMode?: boolean;
|
bufferMode?: boolean;
|
||||||
@@ -135,7 +135,8 @@ export default class RedisCommandsQueue {
|
|||||||
signal: options.signal,
|
signal: options.signal,
|
||||||
listener
|
listener
|
||||||
};
|
};
|
||||||
options.signal.addEventListener('abort', listener, {
|
// AbortSignal type is incorrent
|
||||||
|
(options.signal as any).addEventListener('abort', listener, {
|
||||||
once: true
|
once: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -229,5 +229,5 @@ export default {
|
|||||||
UNWATCH,
|
UNWATCH,
|
||||||
unwatch: UNWATCH,
|
unwatch: UNWATCH,
|
||||||
WAIT,
|
WAIT,
|
||||||
wait: WAIT,
|
wait: WAIT
|
||||||
};
|
};
|
||||||
|
@@ -611,8 +611,9 @@ describe('Client', () => {
|
|||||||
const promise = assert.rejects(client.connect(), ConnectionTimeoutError),
|
const promise = assert.rejects(client.connect(), ConnectionTimeoutError),
|
||||||
start = process.hrtime.bigint();
|
start = process.hrtime.bigint();
|
||||||
|
|
||||||
|
while (process.hrtime.bigint() - start < 1_000_000) {
|
||||||
// block the event loop for 1ms, to make sure the connection will timeout
|
// block the event loop for 1ms, to make sure the connection will timeout
|
||||||
while (process.hrtime.bigint() - start < 1_000_000) {}
|
}
|
||||||
|
|
||||||
await promise;
|
await promise;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@@ -34,16 +34,16 @@ type WithCommands = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type WithModules<M extends RedisModules> = {
|
export type WithModules<M extends RedisModules> = {
|
||||||
[P in keyof M]: {
|
[P in keyof M as M[P] extends never ? never : P]: {
|
||||||
[C in keyof M[P]]: RedisClientCommandSignature<M[P][C]>;
|
[C in keyof M[P]]: RedisClientCommandSignature<M[P][C]>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WithScripts<S extends RedisScripts> = {
|
export type WithScripts<S extends RedisScripts> = {
|
||||||
[P in keyof S]: RedisClientCommandSignature<S[P]>;
|
[P in keyof S as S[P] extends never ? never : P]: RedisClientCommandSignature<S[P]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RedisClientType<M extends RedisModules = {}, S extends RedisScripts = {}> =
|
export type RedisClientType<M extends RedisModules = Record<string, never>, S extends RedisScripts = Record<string, never>> =
|
||||||
RedisClient<M, S> & WithCommands & WithModules<M> & WithScripts<S>;
|
RedisClient<M, S> & WithCommands & WithModules<M> & WithScripts<S>;
|
||||||
|
|
||||||
export type InstantiableRedisClient<M extends RedisModules, S extends RedisScripts> =
|
export type InstantiableRedisClient<M extends RedisModules, S extends RedisScripts> =
|
||||||
@@ -53,12 +53,14 @@ export interface ClientCommandOptions extends QueueCommandOptions {
|
|||||||
isolated?: boolean;
|
isolated?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => void;
|
||||||
|
|
||||||
export default class RedisClient<M extends RedisModules, S extends RedisScripts> extends EventEmitter {
|
export default class RedisClient<M extends RedisModules, S extends RedisScripts> extends EventEmitter {
|
||||||
static commandOptions(options: ClientCommandOptions): CommandOptions<ClientCommandOptions> {
|
static commandOptions(options: ClientCommandOptions): CommandOptions<ClientCommandOptions> {
|
||||||
return commandOptions(options);
|
return commandOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
static extend<M extends RedisModules = {}, S extends RedisScripts = {}>(plugins?: RedisPlugins<M, S>): InstantiableRedisClient<M, S> {
|
static extend<M extends RedisModules = Record<string, never>, S extends RedisScripts = Record<string, never>>(plugins?: RedisPlugins<M, S>): InstantiableRedisClient<M, S> {
|
||||||
const Client = <any>extendWithModulesAndScripts({
|
const Client = <any>extendWithModulesAndScripts({
|
||||||
BaseClass: RedisClient,
|
BaseClass: RedisClient,
|
||||||
modules: plugins?.modules,
|
modules: plugins?.modules,
|
||||||
@@ -74,14 +76,14 @@ export default class RedisClient<M extends RedisModules, S extends RedisScripts>
|
|||||||
return Client;
|
return Client;
|
||||||
}
|
}
|
||||||
|
|
||||||
static create<M extends RedisModules = {}, S extends RedisScripts = {}>(options?: RedisClientOptions<M, S>): RedisClientType<M, S> {
|
static create<M extends RedisModules = Record<string, never>, S extends RedisScripts = Record<string, never>>(options?: RedisClientOptions<M, S>): RedisClientType<M, S> {
|
||||||
return new (RedisClient.extend(options))(options);
|
return new (RedisClient.extend(options))(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
static parseURL(url: string): RedisClientOptions<{}, {}> {
|
static parseURL(url: string): RedisClientOptions<Record<string, never>, Record<string, never>> {
|
||||||
// https://www.iana.org/assignments/uri-schemes/prov/redis
|
// https://www.iana.org/assignments/uri-schemes/prov/redis
|
||||||
const { hostname, port, protocol, username, password, pathname } = new URL(url),
|
const { hostname, port, protocol, username, password, pathname } = new URL(url),
|
||||||
parsed: RedisClientOptions<{}, {}> = {
|
parsed: RedisClientOptions<Record<string, never>, Record<string, never>> = {
|
||||||
socket: {
|
socket: {
|
||||||
host: hostname
|
host: hostname
|
||||||
}
|
}
|
||||||
@@ -245,10 +247,12 @@ export default class RedisClient<M extends RedisModules, S extends RedisScripts>
|
|||||||
|
|
||||||
(this as any).#v4.sendCommand = this.#sendCommand.bind(this);
|
(this as any).#v4.sendCommand = this.#sendCommand.bind(this);
|
||||||
(this as any).sendCommand = (...args: Array<unknown>): void => {
|
(this as any).sendCommand = (...args: Array<unknown>): void => {
|
||||||
const callback = typeof args[args.length - 1] === 'function' ? args[args.length - 1] as Function : undefined,
|
const callback = typeof args[args.length - 1] === 'function' ?
|
||||||
|
args[args.length - 1] as ClientLegacyCallback :
|
||||||
|
undefined,
|
||||||
actualArgs = !callback ? args : args.slice(0, -1);
|
actualArgs = !callback ? args : args.slice(0, -1);
|
||||||
this.#sendCommand(actualArgs.flat() as Array<string>)
|
this.#sendCommand(actualArgs.flat() as Array<string>)
|
||||||
.then((reply: unknown) => {
|
.then((reply: RedisCommandRawReply) => {
|
||||||
if (!callback) return;
|
if (!callback) return;
|
||||||
|
|
||||||
// https://github.com/NodeRedis/node-redis#commands:~:text=minimal%20parsing
|
// https://github.com/NodeRedis/node-redis#commands:~:text=minimal%20parsing
|
||||||
@@ -435,17 +439,12 @@ export default class RedisClient<M extends RedisModules, S extends RedisScripts>
|
|||||||
|
|
||||||
this.#socket.cork();
|
this.#socket.cork();
|
||||||
|
|
||||||
while (true) {
|
while (!this.#socket.writableNeedDrain) {
|
||||||
const args = this.#queue.getCommandToSend();
|
const args = this.#queue.getCommandToSend();
|
||||||
if (args === undefined) break;
|
if (args === undefined) break;
|
||||||
|
|
||||||
let writeResult;
|
|
||||||
for (const toWrite of encodeCommand(args)) {
|
for (const toWrite of encodeCommand(args)) {
|
||||||
writeResult = this.#socket.write(toWrite);
|
this.#socket.write(toWrite);
|
||||||
}
|
|
||||||
|
|
||||||
if (!writeResult) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,16 +11,16 @@ type WithCommands<M extends RedisModules, S extends RedisScripts> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type WithModules<M extends RedisModules, S extends RedisScripts> = {
|
type WithModules<M extends RedisModules, S extends RedisScripts> = {
|
||||||
[P in keyof M]: {
|
[P in keyof M as M[P] extends never ? never : P]: {
|
||||||
[C in keyof M[P]]: RedisClientMultiCommandSignature<M[P][C], M, S>;
|
[C in keyof M[P]]: RedisClientMultiCommandSignature<M[P][C], M, S>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type WithScripts<M extends RedisModules, S extends RedisScripts> = {
|
type WithScripts<M extends RedisModules, S extends RedisScripts> = {
|
||||||
[P in keyof S]: RedisClientMultiCommandSignature<S[P], M, S>
|
[P in keyof S as S[P] extends never ? never : P]: RedisClientMultiCommandSignature<S[P], M, S>
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RedisClientMultiCommandType<M extends RedisModules = {}, S extends RedisScripts = {}> =
|
export type RedisClientMultiCommandType<M extends RedisModules = Record<string, never>, S extends RedisScripts = Record<string, never>> =
|
||||||
RedisClientMultiCommand & WithCommands<M, S> & WithModules<M, S> & WithScripts<M, S>;
|
RedisClientMultiCommand & WithCommands<M, S> & WithModules<M, S> & WithScripts<M, S>;
|
||||||
|
|
||||||
export type RedisClientMultiExecutor = (queue: Array<RedisMultiQueuedCommand>, chainId?: symbol) => Promise<Array<RedisCommandRawReply>>;
|
export type RedisClientMultiExecutor = (queue: Array<RedisMultiQueuedCommand>, chainId?: symbol) => Promise<Array<RedisCommandRawReply>>;
|
||||||
|
@@ -234,7 +234,6 @@ export default class RedisSocket extends EventEmitter {
|
|||||||
|
|
||||||
this.#isOpen = false;
|
this.#isOpen = false;
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fn();
|
await fn();
|
||||||
await this.disconnect(true);
|
await this.disconnect(true);
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import COMMANDS from './commands';
|
import COMMANDS from './commands';
|
||||||
import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisModules, RedisScript, RedisScripts } from '../commands';
|
import { RedisCommand, RedisCommandArguments, RedisCommandReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands';
|
||||||
import { ClientCommandOptions, RedisClientCommandSignature, RedisClientOptions, RedisClientType, WithModules, WithScripts } from '../client';
|
import { ClientCommandOptions, RedisClientCommandSignature, RedisClientOptions, RedisClientType, WithModules, WithScripts } from '../client';
|
||||||
import RedisClusterSlots, { ClusterNode } from './cluster-slots';
|
import RedisClusterSlots, { ClusterNode } from './cluster-slots';
|
||||||
import { extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, extendWithCommands } from '../commander';
|
import { extendWithModulesAndScripts, transformCommandArguments, transformCommandReply, extendWithCommands } from '../commander';
|
||||||
@@ -7,14 +7,9 @@ import { EventEmitter } from 'events';
|
|||||||
import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command';
|
import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command';
|
||||||
import { RedisMultiQueuedCommand } from '../multi-command';
|
import { RedisMultiQueuedCommand } from '../multi-command';
|
||||||
|
|
||||||
export type RedisClusterClientOptions = Omit<RedisClientOptions<{}, {}>, 'modules' | 'scripts'>;
|
export type RedisClusterClientOptions = Omit<RedisClientOptions<Record<string, never>, Record<string, never>>, 'modules' | 'scripts'>;
|
||||||
|
|
||||||
export interface RedisClusterPlugins<M extends RedisModules, S extends RedisScripts> {
|
export interface RedisClusterOptions<M extends RedisModules, S extends RedisScripts> extends RedisPlugins<M, S> {
|
||||||
modules?: M;
|
|
||||||
scripts?: S;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RedisClusterOptions<M extends RedisModules, S extends RedisScripts> extends RedisClusterPlugins<M, S> {
|
|
||||||
rootNodes: Array<RedisClusterClientOptions>;
|
rootNodes: Array<RedisClusterClientOptions>;
|
||||||
defaults?: Partial<RedisClusterClientOptions>;
|
defaults?: Partial<RedisClusterClientOptions>;
|
||||||
useReplicas?: boolean;
|
useReplicas?: boolean;
|
||||||
@@ -25,10 +20,10 @@ type WithCommands = {
|
|||||||
[P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>;
|
[P in keyof typeof COMMANDS]: RedisClientCommandSignature<(typeof COMMANDS)[P]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RedisClusterType<M extends RedisModules = {}, S extends RedisScripts = {}> =
|
export type RedisClusterType<M extends RedisModules = Record<string, never>, S extends RedisScripts = Record<string, never>> =
|
||||||
RedisCluster<M, S> & WithCommands & WithModules<M> & WithScripts<S>;
|
RedisCluster<M, S> & WithCommands & WithModules<M> & WithScripts<S>;
|
||||||
|
|
||||||
export default class RedisCluster<M extends RedisModules = {}, S extends RedisScripts = {}> extends EventEmitter {
|
export default class RedisCluster<M extends RedisModules = Record<string, never>, S extends RedisScripts = Record<string, never>> extends EventEmitter {
|
||||||
static extractFirstKey(command: RedisCommand, originalArgs: Array<unknown>, redisArgs: RedisCommandArguments): string | Buffer | undefined {
|
static extractFirstKey(command: RedisCommand, originalArgs: Array<unknown>, redisArgs: RedisCommandArguments): string | Buffer | undefined {
|
||||||
if (command.FIRST_KEY_INDEX === undefined) {
|
if (command.FIRST_KEY_INDEX === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -39,7 +34,7 @@ export default class RedisCluster<M extends RedisModules = {}, S extends RedisSc
|
|||||||
return command.FIRST_KEY_INDEX(...originalArgs);
|
return command.FIRST_KEY_INDEX(...originalArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static create<M extends RedisModules = {}, S extends RedisScripts = {}>(options?: RedisClusterOptions<M, S>): RedisClusterType<M, S> {
|
static create<M extends RedisModules = Record<string, never>, S extends RedisScripts = Record<string, never>>(options?: RedisClusterOptions<M, S>): RedisClusterType<M, S> {
|
||||||
return new (<any>extendWithModulesAndScripts({
|
return new (<any>extendWithModulesAndScripts({
|
||||||
BaseClass: RedisCluster,
|
BaseClass: RedisCluster,
|
||||||
modules: options?.modules,
|
modules: options?.modules,
|
||||||
|
@@ -12,16 +12,16 @@ type WithCommands<M extends RedisModules, S extends RedisScripts> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type WithModules<M extends RedisModules, S extends RedisScripts> = {
|
type WithModules<M extends RedisModules, S extends RedisScripts> = {
|
||||||
[P in keyof M]: {
|
[P in keyof M as M[P] extends never ? never : P]: {
|
||||||
[C in keyof M[P]]: RedisClusterMultiCommandSignature<M[P][C], M, S>;
|
[C in keyof M[P]]: RedisClusterMultiCommandSignature<M[P][C], M, S>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type WithScripts<M extends RedisModules, S extends RedisScripts> = {
|
type WithScripts<M extends RedisModules, S extends RedisScripts> = {
|
||||||
[P in keyof S]: RedisClusterMultiCommandSignature<S[P], M, S>
|
[P in keyof S as S[P] extends never ? never : P]: RedisClusterMultiCommandSignature<S[P], M, S>
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RedisClusterMultiCommandType<M extends RedisModules = {}, S extends RedisScripts = {}> =
|
export type RedisClusterMultiCommandType<M extends RedisModules = Record<string, never>, S extends RedisScripts = Record<string, never>> =
|
||||||
RedisClusterMultiCommand & WithCommands<M, S> & WithModules<M, S> & WithScripts<M, S>;
|
RedisClusterMultiCommand & WithCommands<M, S> & WithModules<M, S> & WithScripts<M, S>;
|
||||||
|
|
||||||
export type RedisClusterMultiExecutor = (queue: Array<RedisMultiQueuedCommand>, firstKey?: string | Buffer, chainId?: symbol) => Promise<Array<RedisCommandRawReply>>;
|
export type RedisClusterMultiExecutor = (queue: Array<RedisMultiQueuedCommand>, firstKey?: string | Buffer, chainId?: symbol) => Promise<Array<RedisCommandRawReply>>;
|
||||||
|
@@ -6,4 +6,4 @@ export function transformArguments(): RedisCommandArguments {
|
|||||||
return ['COMMAND', 'COUNT'];
|
return ['COMMAND', 'COUNT'];
|
||||||
}
|
}
|
||||||
|
|
||||||
declare function transformReply(): number;
|
export declare function transformReply(): number;
|
||||||
|
@@ -6,4 +6,4 @@ export function transformArguments(args: Array<string>): RedisCommandArguments {
|
|||||||
return ['COMMAND', 'GETKEYS', ...args];
|
return ['COMMAND', 'GETKEYS', ...args];
|
||||||
}
|
}
|
||||||
|
|
||||||
declare function transformReply(): Array<string>;
|
export declare function transformReply(): Array<string>;
|
||||||
|
@@ -2,43 +2,27 @@ import { RedisCommandArguments } from '.';
|
|||||||
|
|
||||||
export const FIRST_KEY_INDEX = 1;
|
export const FIRST_KEY_INDEX = 1;
|
||||||
|
|
||||||
interface EX {
|
type MaximumOneOf<T, K extends keyof T = keyof T> =
|
||||||
|
K extends keyof T ? { [P in K]?: T[K] } & Partial<Record<Exclude<keyof T, K>, never>> : never;
|
||||||
|
|
||||||
|
type SetTTL = MaximumOneOf<{
|
||||||
EX: number;
|
EX: number;
|
||||||
}
|
PX: number;
|
||||||
|
|
||||||
interface PX {
|
|
||||||
PX: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EXAT {
|
|
||||||
EXAT: number;
|
EXAT: number;
|
||||||
}
|
|
||||||
|
|
||||||
interface PXAT {
|
|
||||||
PXAT: number;
|
PXAT: number;
|
||||||
}
|
|
||||||
|
|
||||||
interface KEEPTTL {
|
|
||||||
KEEPTTL: true;
|
KEEPTTL: true;
|
||||||
}
|
}>;
|
||||||
|
|
||||||
type SetTTL = EX | PX | EXAT | PXAT | KEEPTTL | {};
|
type SetGuards = MaximumOneOf<{
|
||||||
|
|
||||||
interface NX {
|
|
||||||
NX: true;
|
NX: true;
|
||||||
}
|
|
||||||
|
|
||||||
interface XX {
|
|
||||||
XX: true;
|
XX: true;
|
||||||
}
|
}>;
|
||||||
|
|
||||||
type SetGuards = NX | XX | {};
|
|
||||||
|
|
||||||
interface SetCommonOptions {
|
interface SetCommonOptions {
|
||||||
GET: true
|
GET?: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetOptions = SetTTL & SetGuards & (SetCommonOptions | {});
|
type SetOptions = SetTTL & SetGuards & SetCommonOptions;
|
||||||
|
|
||||||
export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): RedisCommandArguments {
|
export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): RedisCommandArguments {
|
||||||
const args = ['SET', key, value];
|
const args = ['SET', key, value];
|
||||||
@@ -47,25 +31,25 @@ export function transformArguments(key: string | Buffer, value: string | Buffer,
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('EX' in options) {
|
if (options.EX) {
|
||||||
args.push('EX', options.EX.toString());
|
args.push('EX', options.EX.toString());
|
||||||
} else if ('PX' in options) {
|
} else if (options.PX) {
|
||||||
args.push('PX', options.PX.toString());
|
args.push('PX', options.PX.toString());
|
||||||
} else if ('EXAT' in options) {
|
} else if (options.EXAT) {
|
||||||
args.push('EXAT', options.EXAT.toString());
|
args.push('EXAT', options.EXAT.toString());
|
||||||
} else if ('PXAT' in options) {
|
} else if (options.PXAT) {
|
||||||
args.push('PXAT', options.PXAT.toString());
|
args.push('PXAT', options.PXAT.toString());
|
||||||
} else if ((<KEEPTTL>options).KEEPTTL) {
|
} else if (options.KEEPTTL) {
|
||||||
args.push('KEEPTTL');
|
args.push('KEEPTTL');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((<NX>options).NX) {
|
if (options.NX) {
|
||||||
args.push('NX');
|
args.push('NX');
|
||||||
} else if ((<XX>options).XX) {
|
} else if (options.XX) {
|
||||||
args.push('XX');
|
args.push('XX');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((<SetCommonOptions>options).GET) {
|
if (options.GET) {
|
||||||
args.push('GET');
|
args.push('GET');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2075
package-lock.json
generated
2075
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -14,6 +14,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'",
|
"test": "nyc -r text-summary -r html mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
|
"lint": "eslint ./*.ts ./lib/**/*.ts",
|
||||||
"documentation": "typedoc"
|
"documentation": "typedoc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -26,20 +27,23 @@
|
|||||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||||
"@tsconfig/node12": "^1.0.9",
|
"@tsconfig/node12": "^1.0.9",
|
||||||
"@types/mocha": "^9.0.0",
|
"@types/mocha": "^9.0.0",
|
||||||
"@types/node": "^16.10.3",
|
"@types/node": "^16.11.1",
|
||||||
"@types/sinon": "^10.0.4",
|
"@types/sinon": "^10.0.4",
|
||||||
"@types/which": "^2.0.1",
|
"@types/which": "^2.0.1",
|
||||||
"@types/yallist": "^4.0.1",
|
"@types/yallist": "^4.0.1",
|
||||||
"mocha": "^9.1.2",
|
"@typescript-eslint/eslint-plugin": "^5.1.0",
|
||||||
|
"@typescript-eslint/parser": "^5.1.0",
|
||||||
|
"eslint": "^8.0.1",
|
||||||
|
"mocha": "^9.1.3",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"release-it": "^14.11.6",
|
"release-it": "^14.11.6",
|
||||||
"sinon": "^11.1.2",
|
"sinon": "^11.1.2",
|
||||||
"source-map-support": "^0.5.20",
|
"source-map-support": "^0.5.20",
|
||||||
"ts-node": "^10.3.0",
|
"ts-node": "^10.3.0",
|
||||||
"typedoc": "^0.22.5",
|
"typedoc": "^0.22.6",
|
||||||
"typedoc-github-wiki-theme": "^0.6.0",
|
"typedoc-github-wiki-theme": "^0.6.0",
|
||||||
"typedoc-plugin-markdown": "^3.11.3",
|
"typedoc-plugin-markdown": "^3.11.3",
|
||||||
"typescript": "^4.4.3",
|
"typescript": "^4.4.4",
|
||||||
"which": "^2.0.2"
|
"which": "^2.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
Reference in New Issue
Block a user