diff --git a/.github/release-drafter-config.yml b/.github/release-drafter-base.yml similarity index 100% rename from .github/release-drafter-config.yml rename to .github/release-drafter-base.yml diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 7b3d42bf9f4..8f1a1cec945 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -10,15 +10,13 @@ jobs: documentation: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v3 with: fetch-depth: 1 - name: Use Node.js - uses: actions/setup-node@v2.3.0 + uses: actions/setup-node@v3 - name: Install Packages run: npm ci - - name: Build tests tools - run: npm run build:tests-tools - name: Generate Documentation run: npm run documentation - name: Upload diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 365e9f31d3c..93f0fdff96b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,14 +16,14 @@ jobs: strategy: fail-fast: false matrix: - node-version: ['14', '16', '18', '19'] - redis-version: ['5', '6.0', '6.2', '7.0'] + node-version: ['16', '18', '19', '20'] + redis-version: ['5', '6.0', '6.2', '7.0', '7.2-rc'] steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v3 with: fetch-depth: 1 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.3.0 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Update npm @@ -32,7 +32,7 @@ jobs: - name: Install Packages run: npm ci - name: Build tests tools - run: npm run build:tests-tools + run: npm run build:client && npm run build:test-utils - name: Run Tests run: npm run test -- -- --forbid-only --redis-version=${{ matrix.redis-version }} - name: Upload to Codecov diff --git a/README.md b/README.md index 4fca8d405d4..ec317d0d115 100644 --- a/README.md +++ b/README.md @@ -16,350 +16,14 @@ node-redis is a modern, high performance [Redis](https://redis.io) client for No | Name | Description | |----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | +| [redis](./packages/redis) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | | [@redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@redis/client.svg)](https://www.npmjs.com/package/@redis/client) [![Version](https://img.shields.io/npm/v/@redis/client.svg)](https://www.npmjs.com/package/@redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | | [@redis/bloom](./packages/bloom) | [![Downloads](https://img.shields.io/npm/dm/@redis/bloom.svg)](https://www.npmjs.com/package/@redis/bloom) [![Version](https://img.shields.io/npm/v/@redis/bloom.svg)](https://www.npmjs.com/package/@redis/bloom) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/bloom/) [Redis Bloom](https://oss.redis.com/redisbloom/) commands | | [@redis/graph](./packages/graph) | [![Downloads](https://img.shields.io/npm/dm/@redis/graph.svg)](https://www.npmjs.com/package/@redis/graph) [![Version](https://img.shields.io/npm/v/@redis/graph.svg)](https://www.npmjs.com/package/@redis/graph) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/graph/) [Redis Graph](https://oss.redis.com/redisgraph/) commands | | [@redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@redis/json.svg)](https://www.npmjs.com/package/@redis/json) [![Version](https://img.shields.io/npm/v/@redis/json.svg)](https://www.npmjs.com/package/@redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | -| [@redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Version](https://img.shields.io/npm/v/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [RediSearch](https://oss.redis.com/redisearch/) commands | +| [@redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Version](https://img.shields.io/npm/v/@redis/search.svg)](https://www.npmjs.com/package/@redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [RediSearch](https://oss.redis.com/redisearch/) commands | | [@redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@redis/time-series.svg)](https://www.npmjs.com/package/@redis/time-series) [![Version](https://img.shields.io/npm/v/@redis/time-series.svg)](https://www.npmjs.com/package/@redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | -> :warning: In version 4.1.0 we moved our subpackages from `@node-redis` to `@redis`. If you're just using `npm install redis`, you don't need to do anything—it'll upgrade automatically. If you're using the subpackages directly, you'll need to point to the new scope (e.g. `@redis/client` instead of `@node-redis/client`). - -## Installation - -Start a redis via docker: - -``` bash -docker run -p 6379:6379 -it redis/redis-stack-server:latest -``` - -To install node-redis, simply: - -```bash -npm install redis -``` - -> :warning: The new interface is clean and cool, but if you have an existing codebase, you'll want to read the [migration guide](./docs/v3-to-v4.md). - -Looking for a high-level library to handle object mapping? See [redis-om-node](https://github.com/redis/redis-om-node)! - -## Usage - -### Basic Example - -```typescript -import { createClient } from 'redis'; - -const client = createClient(); - -client.on('error', err => console.log('Redis Client Error', err)); - -await client.connect(); - -await client.set('key', 'value'); -const value = await client.get('key'); -await client.disconnect(); -``` - -The above code connects to localhost on port 6379. To connect to a different host or port, use a connection string in the format `redis[s]://[[username][:password]@][host][:port][/db-number]`: - -```typescript -createClient({ - url: 'redis://alice:foobared@awesome.redis.server:6380' -}); -``` - -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). - -To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean. `client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it isn't (for example when the client is still connecting or reconnecting after a network error). - -### Redis Commands - -There is built-in support for all of the [out-of-the-box Redis commands](https://redis.io/commands). They are exposed using the raw Redis command names (`HSET`, `HGETALL`, etc.) and a friendlier camel-cased version (`hSet`, `hGetAll`, etc.): - -```typescript -// raw Redis commands -await client.HSET('key', 'field', 'value'); -await client.HGETALL('key'); - -// friendly JavaScript commands -await client.hSet('key', 'field', 'value'); -await client.hGetAll('key'); -``` - -Modifiers to commands are specified using a JavaScript object: - -```typescript -await client.set('key', 'value', { - EX: 10, - NX: true -}); -``` - -Replies will be transformed into useful data structures: - -```typescript -await client.hGetAll('key'); // { field1: 'value1', field2: 'value2' } -await client.hVals('key'); // ['value1', 'value2'] -``` - -`Buffer`s are supported as well: - -```typescript -await client.hSet('key', 'field', Buffer.from('value')); // 'OK' -await client.hGetAll( - commandOptions({ returnBuffers: true }), - 'key' -); // { field: } -``` - -### Unsupported Redis Commands - -If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`: - -```typescript -await client.sendCommand(['SET', 'key', 'value', 'NX']); // 'OK' - -await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'field2'] -``` - -### Transactions (Multi/Exec) - -Start a [transaction](https://redis.io/topics/transactions) by calling `.multi()`, then chaining your commands. When you're done, call `.exec()` and you'll get an array back with your results: - -```typescript -await client.set('another-key', 'another-value'); - -const [setKeyReply, otherKeyValue] = await client - .multi() - .set('key', 'value') - .get('another-key') - .exec(); // ['OK', 'another-value'] -``` - -You can also [watch](https://redis.io/topics/transactions#optimistic-locking-using-check-and-set) keys by calling `.watch()`. Your transaction will abort if any of the watched keys change. - -To dig deeper into transactions, check out the [Isolated Execution Guide](./docs/isolated-execution.md). - -### Blocking Commands - -Any command can be run on a new connection by specifying the `isolated` option. The newly created connection is closed when the command's `Promise` is fulfilled. - -This pattern works especially well for blocking commands—such as `BLPOP` and `BLMOVE`: - -```typescript -import { commandOptions } from 'redis'; - -const blPopPromise = client.blPop( - commandOptions({ isolated: true }), - 'key', - 0 -); - -await client.lPush('key', ['1', '2']); - -await blPopPromise; // '2' -``` - -To learn more about isolated execution, check out the [guide](./docs/isolated-execution.md). - -### Pub/Sub - -See the [Pub/Sub overview](./docs/pub-sub.md). - -### Scan Iterator - -[`SCAN`](https://redis.io/commands/scan) results can be looped over using [async iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator): - -```typescript -for await (const key of client.scanIterator()) { - // use the key! - await client.get(key); -} -``` - -This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: - -```typescript -for await (const { field, value } of client.hScanIterator('hash')) {} -for await (const member of client.sScanIterator('set')) {} -for await (const { score, value } of client.zScanIterator('sorted-set')) {} -``` - -You can override the default options by providing a configuration object: - -```typescript -client.scanIterator({ - TYPE: 'string', // `SCAN` only - MATCH: 'patter*', - COUNT: 100 -}); -``` - -### [Programmability](https://redis.io/docs/manual/programmability/) - -Redis provides a programming interface allowing code execution on the redis server. - -#### [Functions](https://redis.io/docs/manual/programmability/functions-intro/) - -The following example retrieves a key in redis, returning the value of the key, incremented by an integer. For example, if your key _foo_ has the value _17_ and we run `add('foo', 25)`, it returns the answer to Life, the Universe and Everything. - -```lua -#!lua name=library - -redis.register_function { - function_name = 'add', - callback = function(keys, args) return redis.call('GET', keys[1]) + args[1] end, - flags = { 'no-writes' } -} -``` - -Here is the same example, but in a format that can be pasted into the `redis-cli`. - -``` -FUNCTION LOAD "#!lua name=library\nredis.register_function{function_name=\"add\", callback=function(keys, args) return redis.call('GET', keys[1])+args[1] end, flags={\"no-writes\"}}" -``` - -Load the prior redis function on the _redis server_ before running the example below. - -```typescript -import { createClient } from 'redis'; - -const client = createClient({ - functions: { - library: { - add: { - NUMBER_OF_KEYS: 1, - transformArguments(key: string, toAdd: number): Array { - return [key, toAdd.toString()]; - }, - transformReply(reply: number): number { - return reply; - } - } - } - } -}); - -await client.connect(); - -await client.set('key', '1'); -await client.library.add('key', 2); // 3 -``` - -#### [Lua Scripts](https://redis.io/docs/manual/programmability/eval-intro/) - -The following is an end-to-end example of the prior concept. - -```typescript -import { createClient, defineScript } from 'redis'; - -const client = createClient({ - scripts: { - add: defineScript({ - NUMBER_OF_KEYS: 1, - SCRIPT: - 'return redis.call("GET", KEYS[1]) + ARGV[1];', - transformArguments(key: string, toAdd: number): Array { - return [key, toAdd.toString()]; - }, - transformReply(reply: number): number { - return reply; - } - }) - } -}); - -await client.connect(); - -await client.set('key', '1'); -await client.add('key', 2); // 3 -``` - -### Disconnecting - -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 -const [ping, get, quit] = await Promise.all([ - client.ping(), - client.get('key'), - client.quit() -]); // ['PONG', null, 'OK'] - -try { - await client.get('key'); -} catch (err) { - // ClosedClient Error -} -``` - -#### `.disconnect()` - -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. - -```typescript -await client.disconnect(); -``` - -### Auto-Pipelining - -Node Redis will automatically pipeline requests that are made during the same "tick". - -```typescript -client.set('Tm9kZSBSZWRpcw==', 'users:1'); -client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw=='); -``` - -Of course, if you don't do something with your Promises you're certain to get [unhandled Promise exceptions](https://nodejs.org/api/process.html#process_event_unhandledrejection). To take advantage of auto-pipelining and handle your Promises, use `Promise.all()`. - -```typescript -await Promise.all([ - client.set('Tm9kZSBSZWRpcw==', 'users:1'), - client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==') -]); -``` - -### Clustering - -Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to connect to a Redis Cluster. - -### Events - -The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes: - -| Name | When | Listener arguments | -|-------------------------|------------------------------------------------------------------------------------|------------------------------------------------------------| -| `connect` | Initiating a connection to the server | *No arguments* | -| `ready` | Client is ready to use | *No arguments* | -| `end` | Connection has been closed (via `.quit()` or `.disconnect()`) | *No arguments* | -| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | -| `reconnecting` | Client is trying to reconnect to the server | *No arguments* | -| `sharded-channel-moved` | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | - -> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and an `error` occurs, that error will be thrown and the Node.js process will exit. See the [`EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details. - -> The client will not emit [any other events](./docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. - -## Supported Redis versions - -Node Redis is supported with the following versions of Redis: - -| Version | Supported | -|---------|--------------------| -| 7.0.z | :heavy_check_mark: | -| 6.2.z | :heavy_check_mark: | -| 6.0.z | :heavy_check_mark: | -| 5.0.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 If you'd like to contribute, check out the [contributing guide](CONTRIBUTING.md). diff --git a/benchmark/lib/ping/ioredis-auto-pipeline.js b/benchmark/lib/ping/ioredis-auto-pipeline.js new file mode 100644 index 00000000000..ee400fe6ca9 --- /dev/null +++ b/benchmark/lib/ping/ioredis-auto-pipeline.js @@ -0,0 +1,20 @@ +import Redis from 'ioredis'; + +export default async (host) => { + const client = new Redis({ + host, + lazyConnect: true, + enableAutoPipelining: true + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + } +}; diff --git a/benchmark/lib/ping/local-resp2.js b/benchmark/lib/ping/local-resp2.js new file mode 100644 index 00000000000..873698a131f --- /dev/null +++ b/benchmark/lib/ping/local-resp2.js @@ -0,0 +1,21 @@ +import { createClient } from 'redis-local'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 2 + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/local-resp3-module-with-flags.js b/benchmark/lib/ping/local-resp3-module-with-flags.js new file mode 100644 index 00000000000..2af849b938b --- /dev/null +++ b/benchmark/lib/ping/local-resp3-module-with-flags.js @@ -0,0 +1,27 @@ +import { createClient } from 'redis-local'; +import PING from 'redis-local/dist/lib/commands/PING.js'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 3, + modules: { + module: { + ping: PING.default + } + } + }); + + await client.connect(); + + return { + benchmark() { + return client.withFlags({}).module.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/local-resp3-module.js b/benchmark/lib/ping/local-resp3-module.js new file mode 100644 index 00000000000..66f6e3ec291 --- /dev/null +++ b/benchmark/lib/ping/local-resp3-module.js @@ -0,0 +1,27 @@ +import { createClient } from 'redis-local'; +import PING from 'redis-local/dist/lib/commands/PING.js'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 3, + modules: { + module: { + ping: PING.default + } + } + }); + + await client.connect(); + + return { + benchmark() { + return client.module.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/local-resp3.js b/benchmark/lib/ping/local-resp3.js new file mode 100644 index 00000000000..a4ee4f24a2a --- /dev/null +++ b/benchmark/lib/ping/local-resp3.js @@ -0,0 +1,21 @@ +import { createClient } from 'redis-local'; + +export default async (host) => { + const client = createClient({ + socket: { + host + }, + RESP: 3 + }); + + await client.connect(); + + return { + benchmark() { + return client.ping(); + }, + teardown() { + return client.disconnect(); + } + }; +}; diff --git a/benchmark/lib/ping/v4.js b/benchmark/lib/ping/v4.js index 69aa3c06929..c570aa1477f 100644 --- a/benchmark/lib/ping/v4.js +++ b/benchmark/lib/ping/v4.js @@ -1,4 +1,4 @@ -import { createClient } from '@redis/client'; +import { createClient } from 'redis-v4'; export default async (host) => { const client = createClient({ diff --git a/benchmark/lib/runner.js b/benchmark/lib/runner.js index a96ff55cab0..3787cbabd1b 100644 --- a/benchmark/lib/runner.js +++ b/benchmark/lib/runner.js @@ -71,13 +71,13 @@ const benchmarkStart = process.hrtime.bigint(), histogram = await run(times), benchmarkNanoseconds = process.hrtime.bigint() - benchmarkStart, json = { - timestamp, + // timestamp, operationsPerSecond: times / Number(benchmarkNanoseconds) * 1_000_000_000, - p0: histogram.getValueAtPercentile(0), - p50: histogram.getValueAtPercentile(50), - p95: histogram.getValueAtPercentile(95), - p99: histogram.getValueAtPercentile(99), - p100: histogram.getValueAtPercentile(100) + // p0: histogram.getValueAtPercentile(0), + // p50: histogram.getValueAtPercentile(50), + // p95: histogram.getValueAtPercentile(95), + // p99: histogram.getValueAtPercentile(99), + // p100: histogram.getValueAtPercentile(100) }; console.log(`[${basename(path)}]:`); console.table(json); diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 828b42803eb..30114847134 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -1,16 +1,17 @@ { "name": "@redis/client-benchmark", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@redis/client-benchmark", "dependencies": { - "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.3.0", - "redis-v3": "npm:redis@3.1.2", - "yargs": "17.6.2" + "ioredis": "5", + "redis-local": "file:../packages/client", + "redis-v3": "npm:redis@3", + "redis-v4": "npm:redis@4", + "yargs": "17.7.1" } }, "node_modules/@assemblyscript/loader": { @@ -19,14 +20,22 @@ "integrity": "sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==" }, "node_modules/@ioredis/commands": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.1.1.tgz", - "integrity": "sha512-fsR4P/ROllzf/7lXYyElUJCheWdTJVJvOTps8v9IWKFATxR61ANOlnoPqhH099xYLrJGpc2ZQ28B3rMeUt5VQg==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } }, "node_modules/@redis/client": { - "version": "1.5.0", - "resolved": "file:../packages/client", - "license": "MIT", + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.7.tgz", + "integrity": "sha512-gaOBOuJPjK5fGtxSseaKgSvjiZXQCdLlGg9WYQst+/GRUjmXaiB5kVkeQMRtPc7Q2t93XZcJfBMSwzs/XS9UZw==", "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -36,6 +45,38 @@ "node": ">=14" } }, + "node_modules/@redis/graph": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", + "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", + "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz", + "integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz", + "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -181,9 +222,9 @@ } }, "node_modules/ioredis": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.0.tgz", - "integrity": "sha512-Id9jKHhsILuIZpHc61QkagfVdUj2Rag5GzG1TGEvRNeM7dtTOjICgjC+tvqYxi//PuX2wjQ+Xjva2ONBuf92Pw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", + "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", @@ -214,12 +255,12 @@ "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" }, "node_modules/ms": { "version": "2.1.2", @@ -239,15 +280,29 @@ "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", "engines": { "node": ">=4" } }, + "node_modules/redis-local": { + "name": "@redis/client", + "version": "1.5.6", + "resolved": "file:../packages/client", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/redis-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", "dependencies": { "redis-errors": "^1.0.0" }, @@ -282,10 +337,24 @@ "node": ">=0.10" } }, + "node_modules/redis-v4": { + "name": "redis", + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.6.tgz", + "integrity": "sha512-aLs2fuBFV/VJ28oLBqYykfnhGGkFxvx0HdCEBYdJ99FFbSEMZ7c1nVKwR6ZRv+7bb7JnC0mmCzaqu8frgOYhpA==", + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.7", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.2", + "@redis/time-series": "1.0.4" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "engines": { "node": ">=0.10.0" } @@ -349,9 +418,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -373,258 +442,5 @@ "node": ">=12" } } - }, - "dependencies": { - "@assemblyscript/loader": { - "version": "0.19.23", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.19.23.tgz", - "integrity": "sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==" - }, - "@ioredis/commands": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.1.1.tgz", - "integrity": "sha512-fsR4P/ROllzf/7lXYyElUJCheWdTJVJvOTps8v9IWKFATxR61ANOlnoPqhH099xYLrJGpc2ZQ28B3rMeUt5VQg==" - }, - "@redis/client": { - "version": "1.5.0", - "requires": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==" - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "hdr-histogram-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-3.0.0.tgz", - "integrity": "sha512-/EpvQI2/Z98mNFYEnlqJ8Ogful8OpArLG/6Tf2bPnkutBVLIeMVNHjk1ZDfshF2BUweipzbk+dB1hgSB7SIakw==", - "requires": { - "@assemblyscript/loader": "^0.19.21", - "base64-js": "^1.2.0", - "pako": "^1.0.3" - } - }, - "ioredis": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.0.tgz", - "integrity": "sha512-Id9jKHhsILuIZpHc61QkagfVdUj2Rag5GzG1TGEvRNeM7dtTOjICgjC+tvqYxi//PuX2wjQ+Xjva2ONBuf92Pw==", - "requires": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, - "redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - }, - "redis-v3": { - "version": "npm:redis@3.1.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", - "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", - "requires": { - "denque": "^1.5.0", - "redis-commands": "^1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - }, - "dependencies": { - "denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" - } } } diff --git a/benchmark/package.json b/benchmark/package.json index 79b0facda83..73acf9d0f1c 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -7,10 +7,11 @@ "start": "node ." }, "dependencies": { - "@redis/client": "../packages/client", "hdr-histogram-js": "3.0.0", - "ioredis": "5.3.0", - "redis-v3": "npm:redis@3.1.2", - "yargs": "17.6.2" + "ioredis": "5", + "redis-local": "file:../packages/client", + "redis-v3": "npm:redis@3", + "redis-v4": "npm:redis@4", + "yargs": "17.7.1" } } diff --git a/docs/RESP3.md b/docs/RESP3.md new file mode 100644 index 00000000000..ed45ac8c87f --- /dev/null +++ b/docs/RESP3.md @@ -0,0 +1,30 @@ +# RESP3 => JS Type Mappings: + +- Null (`_`) => `null` +- Boolean (`#`) => `boolean` +- Number (`:`) => `number | string` +- Big Number (`(`) => `BigInt | string` +- Double (`,`) => `number | string` +- Simple String (`+`) => `string | Buffer` +- Blob String (`$`) => `string | Buffer` +- Verbatim String (`=`) => `string | Buffer | VerbatimString` (TODO: `VerbatimString` typedoc link) +- Simple Error (`-`) => `ErrorReply` +- Blob Error (`!`) => `ErrorReply` +- Array (`*`) => `Array` +- Set (`~`) => `Array | Set` +- Map (`%`) => `object | Map | Array` +- Push (`>`) => `Array` => PubSub push/`'push'` event + +> NOTE: the first type is the default type + +## Map keys and Set members + +When decoding Map to `Map | object` or Set to `Set`, keys/members of type "Simple String" or "Blob String" will be decoded as `string`s (ignoring flags) to allow lookup by type. If you need them as `Buffer`s, make sure to decode `Map`s/`Set`s as `Array`s. + +## Not Implemented + +These parts of RESP3 are not yet implemented in Redis itself (at the time of writing), so are not yet implemented in the Node-Redis client either: + +- [Attribute type](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#attribute-type) +- [Streamed strings](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#streamed-strings) +- [Streamed aggregated data types](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#streamed-aggregated-data-types) diff --git a/docs/clustering.md b/docs/clustering.md index 28ea0e2964c..b5594e49a76 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -32,7 +32,7 @@ const value = await cluster.get('key'); | 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 | +| 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 | | minimizeConnections | `false` | When `true`, `.connect()` will only discover the cluster topology, without actually connecting to all the nodes. Useful for short-term or Pub/Sub-only connections. | @@ -105,9 +105,11 @@ createCluster({ ## Command Routing +TODO request response policy + ### Commands that operate on Redis Keys -Commands such as `GET`, `SET`, etc. are routed by the first key, for instance `MGET 1 2 3` will be routed by the key `1`. +Commands such as `GET`, `SET`, etc. are routed by the first key specified. For example `MGET 1 2 3` will be routed by the key `1`. ### [Server Commands](https://redis.io/commands#server) @@ -115,4 +117,4 @@ Admin commands such as `MEMORY STATS`, `FLUSHALL`, etc. are not attached to the ### "Forwarded Commands" -Certain commands (e.g. `PUBLISH`) are forwarded to other cluster nodes by the Redis server. This client sends these commands to a random node in order to spread the load across the cluster. +Certain commands (e.g. `PUBLISH`) are forwarded to other cluster nodes by the Redis server. The client sends these commands to a random node in order to spread the load across the cluster. diff --git a/docs/todo.md b/docs/todo.md new file mode 100644 index 00000000000..ec64adf9d28 --- /dev/null +++ b/docs/todo.md @@ -0,0 +1,14 @@ +# Missing functionality + +- `HEXISTS`: accepts one field only, should be the same as `EXISTS` + +# Replies + +`String` -> `Double`: +- `INCRBYFLOAT` +- `HINCRBYFLOAT` +- `GEODIST` + +`Number` -> `Boolean`: +- `HSETNX` (deprecated) +- `SCRIPT EXISTS` diff --git a/docs/v4-to-v5.md b/docs/v4-to-v5.md new file mode 100644 index 00000000000..481bd95c696 --- /dev/null +++ b/docs/v4-to-v5.md @@ -0,0 +1,91 @@ +# v4 to v5 migration guide + +## Command Options + +In v4, command options are passed as a first optional argument: + +```javascript +await client.get('key'); // `string | null` +await client.get(client.commandOptions({ returnBuffers: true }), 'key'); // `Buffer | null` +``` + +This has a couple of flaws: +1. The argument types are checked in runtime, which is a performance hit. +2. Code suggestions are less readable/usable, due to "function overloading". +3. Overall, "user code" is not as readable as it could be. + +### The new API for v5 + +With the new API, instead of passing the options directly to the commands we use a "proxy client" to store them: + +```javascript +await client.get('key'); // `string | null` + +const proxyClient = client.withCommandOptions({ + flags: { + [TYPES.BLOB_STRING]: Buffer + } +}); + +await proxyClient.get('key'); // `Buffer | null` +``` + +`withCommandOptions` can be used to override all of the command options, without reusing any existing ones. + +To override just a specific option, use the following functions: +- `withFlags` - override `flags` only. +- `asap` - override `asap` to `true`. +- `isolated` - override `isolated` to `true`. + +## Quit VS Disconnect + +The `QUIT` command has been deprecated in Redis 7.2 and should now also be considered deprecated in Node-Redis. Instead of sending a `QUIT` command to the server, the client can simply close the network connection. + +Rather than using `client.quit()`, your code should use `client.close()` or `client.disconnect()`. + +TODO difference between `close` and `disconnect`... + +## Scan Iterators + +TODO + +Yields chunks instead of individual items: + +```javascript +for await (const chunk of client.scanIterator()) { + // `chunk` type is `Array` + // will allow multi keys operations + await client.del(chunk); +} +``` + +## Commands + +Some command arguments/replies have changed to align more closely to data types returned by Redis: + +- `ACL GETUSER`: `selectors` +- `CLIENT KILL`: `enum ClientKillFilters` -> `const CLIENT_KILL_FILTERS` [^enum-to-constants] +- `CLUSTER FAILOVER`: `enum FailoverModes` -> `const FAILOVER_MODES` [^enum-to-constants] +- `LCS IDX`: `length` has been changed to `len`, `matches` has been changed from `Array<{ key1: RangeReply; key2: RangeReply; }>` to `Array<[key1: RangeReply, key2: RangeReply]>` +- `HEXISTS`: `boolean` -> `number` [^boolean-to-number] +- `HRANDFIELD_COUNT_WITHVALUES`: `Record` -> `Array<{ field: BlobString; value: BlobString; }>` (it can return duplicates). +- `SCAN`, `HSCAN`, `SSCAN`, and `ZSCAN`: cursor type is `string` instead of `number`? +- `HSETNX`: `boolean` -> `number` [^boolean-to-number] +- `ZINTER`: instead of `client.ZINTER('11, { WEIGHTS: [1] })` use `client.ZINTER({ key: '1', weight: 1 }])` +- `SETNX`: `boolean` -> `number` [^boolean-to-number] +- `COPY`: `destinationDb` -> `DB`, `replace` -> `REPLACE`, `boolean` -> `number` [^boolean-to-number] +- `EXPIRE`: `boolean` -> `number` [^boolean-to-number] +- `EXPIREAT`: `boolean` -> `number` [^boolean-to-number] +- `MOVE`: `boolean` -> `number` [^boolean-to-number] +- `PEXPIRE`: `boolean` -> `number` [^boolean-to-number] +- `PEXPIREAT`: `boolean` -> `number` [^boolean-to-number] +- `RENAMENX`: `boolean` -> `number` [^boolean-to-number] +- `HSCAN`: `tuples` has been renamed to `entries` +- `PFADD`: `boolean` -> `number` [^boolean-to-number] +- `SCRIPT EXISTS`: `Array` -> `Array` [^boolean-to-number] +- `SISMEMBER`: `boolean` -> `number` [^boolean-to-number] +- `SMISMEMBER`: `Array` -> `Array` [^boolean-to-number] + +[^enum-to-constants]: TODO + +[^boolean-to-number]: TODO diff --git a/docs/v5.md b/docs/v5.md new file mode 100644 index 00000000000..a38d7800d7c --- /dev/null +++ b/docs/v5.md @@ -0,0 +1,39 @@ +# RESP3 Support + +[RESP3](./RESP3.md) + +```javascript +const client = createClient({ + RESP: 3 +}); + +client.on('error', err => console.error(err)); + +await client.connect(); + +client.hGetAll('key'); // Record + +client.withFlags({ + [TYPES.MAP]: Map +}).hGetAll('key'); // Map + +client.withFlags({ + [TYPES.MAP]: Map, + [TYPES.BLOB_STRING]: Buffer +}).hGetAll('key'); // Map +``` + +# `multi.exec<'typed'>` / `multi.execTyped` + +We have introduced the ability to perform a "typed" `MULTI`/`EXEC` transaction. Rather than returning `Array`, a transaction invoked with `.exec<'typed'>` will return types appropriate to the commands in the transaction where possible: + +```javascript +const multi = client.multi().ping(); +await multi.exec(); // Array +await multi.exec<'typed'>(); // [string] +await multi.execTyped(); // [string] +``` + +# Request & Reply Policies + +see [here](../docs/clustering.md#command-routing). diff --git a/examples/package.json b/examples/package.json index 65ba1442f7e..91120774d94 100644 --- a/examples/package.json +++ b/examples/package.json @@ -6,7 +6,7 @@ "private": true, "type": "module", "dependencies": { - "redis": "../" + "redis": "../packages/client" } } diff --git a/index.ts b/index.ts deleted file mode 100644 index 5b5a6e81294..00000000000 --- a/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { - RedisModules, - RedisFunctions, - RedisScripts, - createClient as _createClient, - RedisClientOptions, - RedisClientType as _RedisClientType, - createCluster as _createCluster, - RedisClusterOptions, - RedisClusterType as _RedisClusterType -} from '@redis/client'; -import RedisBloomModules from '@redis/bloom'; -import RedisGraph from '@redis/graph'; -import RedisJSON from '@redis/json'; -import RediSearch from '@redis/search'; -import RedisTimeSeries from '@redis/time-series'; - -export * from '@redis/client'; -export * from '@redis/bloom'; -export * from '@redis/graph'; -export * from '@redis/json'; -export * from '@redis/search'; -export * from '@redis/time-series'; - -const modules = { - ...RedisBloomModules, - graph: RedisGraph, - json: RedisJSON, - ft: RediSearch, - ts: RedisTimeSeries -}; - -export type RedisDefaultModules = typeof modules; - -export type RedisClientType< - M extends RedisModules = RedisDefaultModules, - F extends RedisFunctions = Record, - S extends RedisScripts = Record -> = _RedisClientType; - -export function createClient< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts ->( - options?: RedisClientOptions -): _RedisClientType { - return _createClient({ - ...options, - modules: { - ...modules, - ...(options?.modules as M) - } - }); -} - -export type RedisClusterType< - M extends RedisModules = RedisDefaultModules, - F extends RedisFunctions = Record, - S extends RedisScripts = Record -> = _RedisClusterType; - -export function createCluster< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts ->( - options: RedisClusterOptions -): RedisClusterType { - return _createCluster({ - ...options, - modules: { - ...modules, - ...(options?.modules as M) - } - }); -} diff --git a/package-lock.json b/package-lock.json index 49d7aea5d14..50671184fd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,38 +1,25 @@ { - "name": "redis", - "version": "4.6.6", + "name": "redis-monorepo", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "redis", - "version": "4.6.6", - "license": "MIT", + "name": "redis-monorepo", "workspaces": [ "./packages/*" ], - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.5.7", - "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.2", - "@redis/time-series": "1.0.4" - }, "devDependencies": { - "@tsconfig/node14": "^1.0.3", - "gh-pages": "^5.0.0", - "release-it": "^15.6.0", - "typescript": "^4.9.5" + "@tsconfig/node16": "^1.0.3", + "gh-pages": "^5.0.0" } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -40,9 +27,9 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", "dev": true, "dependencies": { "@babel/highlight": "^7.18.6" @@ -52,30 +39,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", + "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", - "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", + "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", + "@babel/helper-compilation-targets": "^7.21.4", + "@babel/helper-module-transforms": "^7.21.2", "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.0", + "@babel/parser": "^7.21.4", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0", + "@babel/traverse": "^7.21.4", + "@babel/types": "^7.21.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -91,12 +78,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", - "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", + "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", "dev": true, "dependencies": { - "@babel/types": "^7.21.0", + "@babel/types": "^7.21.4", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -105,28 +92,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", + "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", + "@babel/compat-data": "^7.21.4", + "@babel/helper-validator-option": "^7.21.0", "browserslist": "^4.21.3", "lru-cache": "^5.1.1", "semver": "^6.3.0" @@ -173,12 +146,12 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.21.4" }, "engines": { "node": ">=6.9.0" @@ -323,15 +296,6 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -354,9 +318,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", + "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -380,19 +344,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", - "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", + "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.1", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.2", - "@babel/types": "^7.21.2", + "@babel/parser": "^7.21.4", + "@babel/types": "^7.21.4", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -410,9 +374,9 @@ } }, "node_modules/@babel/types": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", - "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", + "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.19.4", @@ -445,15 +409,39 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", + "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", + "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.5.1", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -468,6 +456,15 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/js": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", + "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -579,13 +576,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -610,21 +608,27 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -719,18 +723,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.0.0.tgz", + "integrity": "sha512-V8BVJGN0ZmMlURF55VFHFd/L92XQQ43KvFjNmY1IYbCN3V/h/uUFV6iQi19WEHM395Nn+1qhUbViCAD/1czzog==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz", - "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz", + "integrity": "sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==", "dev": true, "dependencies": { - "@octokit/types": "^8.0.0" + "@octokit/types": "^9.0.0" }, "engines": { "node": ">= 14" @@ -739,21 +743,6 @@ "@octokit/core": ">=4" } }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", - "dev": true - }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dev": true, - "dependencies": { - "@octokit/openapi-types": "^14.0.0" - } - }, "node_modules/@octokit/plugin-request-log": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", @@ -764,12 +753,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz", - "integrity": "sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.0.1.tgz", + "integrity": "sha512-pnCaLwZBudK5xCdrR823xHGNgqOzRnJ/mpC/76YPpNP7DybdsJtP7mdOwh+wYZxK5jqeQuhu59ogMI4NRlBUvA==", "dev": true, "dependencies": { - "@octokit/types": "^8.1.1", + "@octokit/types": "^9.0.0", "deprecation": "^2.3.1" }, "engines": { @@ -779,21 +768,6 @@ "@octokit/core": ">=3" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==", - "dev": true - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dev": true, - "dependencies": { - "@octokit/openapi-types": "^14.0.0" - } - }, "node_modules/@octokit/request": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.3.tgz", @@ -846,27 +820,36 @@ } }, "node_modules/@octokit/rest": { - "version": "19.0.5", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz", - "integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.7.tgz", + "integrity": "sha512-HRtSfjrWmWVNp2uAkEpQnuGMJsu/+dBr47dRc5QVgsCbnIc1+GFEaoKBWkYG+zjrsHpSqcAElMio+n10c0b5JA==", "dev": true, "dependencies": { "@octokit/core": "^4.1.0", - "@octokit/plugin-paginate-rest": "^5.0.0", + "@octokit/plugin-paginate-rest": "^6.0.0", "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^6.7.0" + "@octokit/plugin-rest-endpoint-methods": "^7.0.0" }, "engines": { "node": ">= 14" } }, "node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.1.2.tgz", + "integrity": "sha512-LPbJIuu1WNoRHbN4UMysEdlissRFpTCWyoKT7kHPufI8T+XX33/qilfMWJo3mCOjNIKu0+43oSQPf+HJa0+TTQ==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^16.0.0" + "@octokit/openapi-types": "^17.0.0" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "engines": { + "node": ">=12.22.0" } }, "node_modules/@pnpm/network.ca-file": { @@ -881,12 +864,19 @@ "node": ">=12.22.0" } }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, "node_modules/@pnpm/npm-conf": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.5.tgz", - "integrity": "sha512-hD8ml183638O3R6/Txrh0L8VzGOrFXgRtRDG4qQC4tONdZ5Z1M+tlUUDUvrjYdmK6G+JTBTeaCLMna11cXzi8A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.1.1.tgz", + "integrity": "sha512-yfRcuupmxxeDOSxvw4g+wFCrGiPD0L32f5WMzqMXp7Rl93EOCdFiDcaSNnZ10Up9GdNqkj70UTa8hfhPFphaZA==", "dev": true, "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" }, @@ -935,9 +925,9 @@ } }, "node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "dependencies": { "type-detect": "4.0.8" @@ -952,10 +942,19 @@ "@sinonjs/commons": "^2.0.0" } }, + "node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/@sinonjs/samsam": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", - "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, "dependencies": { "@sinonjs/commons": "^2.0.0", @@ -963,6 +962,15 @@ "type-detect": "^4.0.8" } }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/@sinonjs/text-encoding": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", @@ -1033,9 +1041,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.1.tgz", - "integrity": "sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ==", + "version": "18.16.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.1.tgz", + "integrity": "sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==", "dev": true }, "node_modules/@types/semver": { @@ -1045,9 +1053,9 @@ "dev": true }, "node_modules/@types/sinon": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", - "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", + "version": "10.0.14", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.14.tgz", + "integrity": "sha512-mn72up6cjaMyMuaPaa/AwKf6WtsSRysQC7wxFkCm1XcOKXPM1z+5Y4H5wjIVBz4gdAkjvZxVVfjA6ba1nHr5WQ==", "dev": true, "dependencies": { "@types/sinonjs__fake-timers": "*" @@ -1066,9 +1074,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -1081,19 +1089,19 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz", - "integrity": "sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==", + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz", + "integrity": "sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/type-utils": "5.53.0", - "@typescript-eslint/utils": "5.53.0", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.1", + "@typescript-eslint/type-utils": "5.59.1", + "@typescript-eslint/utils": "5.59.1", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" }, @@ -1127,9 +1135,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1141,21 +1149,15 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/parser": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.53.0.tgz", - "integrity": "sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==", + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz", + "integrity": "sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/scope-manager": "5.59.1", + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/typescript-estree": "5.59.1", "debug": "^4.3.4" }, "engines": { @@ -1175,13 +1177,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", - "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz", + "integrity": "sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/visitor-keys": "5.53.0" + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/visitor-keys": "5.59.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1192,13 +1194,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz", - "integrity": "sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==", + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.1.tgz", + "integrity": "sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.53.0", - "@typescript-eslint/utils": "5.53.0", + "@typescript-eslint/typescript-estree": "5.59.1", + "@typescript-eslint/utils": "5.59.1", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1219,9 +1221,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", - "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.1.tgz", + "integrity": "sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1232,13 +1234,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", - "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz", + "integrity": "sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/visitor-keys": "5.53.0", + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/visitor-keys": "5.59.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1300,9 +1302,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1314,25 +1316,19 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/utils": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", - "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.1.tgz", + "integrity": "sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA==", "dev": true, "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/scope-manager": "5.59.1", + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/typescript-estree": "5.59.1", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" }, "engines": { @@ -1359,9 +1355,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1373,19 +1369,13 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", - "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz", + "integrity": "sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/types": "5.59.1", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1476,15 +1466,6 @@ "string-width": "^4.1.0" } }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-align/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1505,18 +1486,6 @@ "node": ">=8" } }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -1527,9 +1496,9 @@ } }, "node_modules/ansi-escapes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.0.0.tgz", - "integrity": "sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", "dev": true, "dependencies": { "type-fest": "^3.0.0" @@ -1541,16 +1510,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.9.0.tgz", + "integrity": "sha512-hR8JP2e8UiH7SME5JZjsobBlEiatFoxpzCP+R3ZeCo7kAaG1jXQE5X/buLzogM6GJu8le9Y4OcfNuIQX0rZskA==", "dev": true, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/ansi-sequence-parser": { @@ -1560,12 +1538,15 @@ "dev": true }, "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -1614,6 +1595,19 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -1725,6 +1719,15 @@ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "dev": true }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1745,10 +1748,33 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/boxen": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.1.tgz", - "integrity": "sha512-8k2eH6SRAK00NDl1iX5q17RJ8rfl53TajdYxE3ssMLehbg487dEVgsad4pIsZb/QqBgYWIl6JOauMTLGX2Kpkw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.2.tgz", + "integrity": "sha512-1Z4UJabXUP1/R9rLpoU3O2lEMnG3pPLAs/ZD2lF3t2q7qD5lM8rqbtnvtvm4N0wEyNlE+9yZVTVAGmd1V5jabg==", "dev": true, "dependencies": { "ansi-align": "^3.0.1", @@ -1779,6 +1805,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boxen/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/boxen/node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -1791,6 +1829,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1877,10 +1927,25 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "engines": { "node": ">= 0.8" @@ -1896,9 +1961,9 @@ } }, "node_modules/cacheable-request": { - "version": "10.2.8", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.8.tgz", - "integrity": "sha512-IDVO5MJ4LItE6HKFQTqT2ocAQsisOoCTUDu1ddCmnhyiwFQjXNPp4081Xj23N4tO+AFEFNzGuNEf/c8Gwwt15A==", + "version": "10.2.10", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.10.tgz", + "integrity": "sha512-v6WB+Epm/qO4Hdlio/sfUn69r5Shgh39SsE9DSd4bIezP0mblOlObI+I0kUEM7J0JFc+I7pSeMeYaOYtX1N/VQ==", "dev": true, "dependencies": { "@types/http-cache-semantics": "^4.0.1", @@ -1960,9 +2025,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001457", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", - "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", + "version": "1.0.30001481", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz", + "integrity": "sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==", "dev": true, "funding": [ { @@ -1972,16 +2037,24 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, "node_modules/chalk": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", - "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -2084,9 +2157,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.8.0.tgz", + "integrity": "sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ==", "dev": true, "engines": { "node": ">=6" @@ -2105,38 +2178,17 @@ } }, "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=12" } }, "node_modules/cliui/node_modules/emoji-regex": { @@ -2159,22 +2211,10 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", @@ -2182,7 +2222,10 @@ "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/clone": { @@ -2286,9 +2329,9 @@ "dev": true }, "node_modules/cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", + "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", "dev": true, "dependencies": { "import-fresh": "^3.2.1", @@ -2298,6 +2341,9 @@ }, "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" } }, "node_modules/create-require": { @@ -2424,6 +2470,40 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", @@ -2461,12 +2541,15 @@ } }, "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-properties": { @@ -2486,15 +2569,15 @@ } }, "node_modules/degenerator": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz", - "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.4.tgz", + "integrity": "sha512-Z66uPeBfHZAHVmue3HPfyKu2Q0rC2cRxbTOsvmU/po5fvvcx27W4mIu9n0PUlQih4oUYvcG1BsbtVv8x7KDOSw==", "dev": true, "dependencies": { "ast-types": "^0.13.2", "escodegen": "^1.8.1", "esprima": "^4.0.0", - "vm2": "^3.9.8" + "vm2": "^3.9.17" }, "engines": { "node": ">= 6" @@ -2570,9 +2653,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.311", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.311.tgz", - "integrity": "sha512-RoDlZufvrtr2Nx3Yx5MB8jX3aHIxm8nRWPJm3yVvyHmyKaRvn90RjzB6hNnt0AkhS3IInJdyRfQb4mWhPvUjVw==", + "version": "1.4.373", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.373.tgz", + "integrity": "sha512-whGyixOVSRlyOBQDsRH9xltFaMij2/+DQRdaYahCq0P/fiVnAVGaW7OVsFnEjze/qUo298ez9C46gnALpo6ukg==", "dev": true }, "node_modules/email-addresses": { @@ -2597,18 +2680,18 @@ } }, "node_modules/es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", "dev": true, "dependencies": { + "array-buffer-byte-length": "^1.0.0", "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", @@ -2616,8 +2699,8 @@ "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", @@ -2625,11 +2708,12 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", + "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.4.3", "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", "string.prototype.trimend": "^1.0.6", "string.prototype.trimstart": "^1.0.6", "typed-array-length": "^1.0.4", @@ -2728,15 +2812,12 @@ } }, "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, "node_modules/escodegen": { @@ -2813,12 +2894,15 @@ } }, "node_modules/eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", - "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz", + "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.4.1", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.2", + "@eslint/js": "8.39.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -2828,11 +2912,10 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.0", + "espree": "^9.5.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", @@ -2853,7 +2936,6 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -2881,80 +2963,16 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", + "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/escape-string-regexp": { @@ -2970,9 +2988,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -2980,6 +2998,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -3052,27 +3073,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", + "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", "dev": true, "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3095,9 +3104,9 @@ } }, "node_modules/esquery": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", - "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -3155,14 +3164,14 @@ } }, "node_modules/execa": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", - "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.1", - "human-signals": "^3.0.1", + "human-signals": "^4.3.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", @@ -3171,12 +3180,24 @@ "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/execa/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -3285,6 +3306,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3512,30 +3557,6 @@ "node": ">=0.8.0" } }, - "node_modules/ftp/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - }, - "node_modules/ftp/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/ftp/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3775,18 +3796,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globalthis": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", @@ -3831,15 +3840,15 @@ } }, "node_modules/got": { - "version": "12.5.3", - "resolved": "https://registry.npmjs.org/got/-/got-12.5.3.tgz", - "integrity": "sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w==", + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.0.tgz", + "integrity": "sha512-WTcaQ963xV97MN3x0/CbAriXFZcXCfgxVp91I+Ze6pawQOa7SgzwSx2zIJJsX+kTajMnVs0xcFD1TxZKFqhdnQ==", "dev": true, "dependencies": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.1", + "cacheable-request": "^10.2.8", "decompress-response": "^6.0.0", "form-data-encoder": "^2.1.2", "get-stream": "^6.0.1", @@ -3856,9 +3865,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "node_modules/grapheme-splitter": { @@ -3976,18 +3985,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hasha/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/hasha/node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -4075,12 +4072,12 @@ } }, "node_modules/human-signals": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", - "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", "dev": true, "engines": { - "node": ">=12.20.0" + "node": ">=14.18.0" } }, "node_modules/iconv-lite": { @@ -4193,29 +4190,68 @@ } }, "node_modules/inquirer": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.4.tgz", - "integrity": "sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==", + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.1.5.tgz", + "integrity": "sha512-3ygAIh8gcZavV9bj6MTdYddG2zPSYswP808fKS46NOwlF0zZljVpnLCHODDqItWJDbDpLb3aouAxGaJbkxoppA==", "dev": true, "dependencies": { "ansi-escapes": "^6.0.0", - "chalk": "^5.1.2", + "chalk": "^5.2.0", "cli-cursor": "^4.0.0", "cli-width": "^4.0.0", "external-editor": "^3.0.3", "figures": "^5.0.0", "lodash": "^4.17.21", - "mute-stream": "0.0.8", + "mute-stream": "1.0.0", "ora": "^6.1.2", "run-async": "^2.4.0", - "rxjs": "^7.5.7", + "rxjs": "^7.8.0", "string-width": "^5.1.2", "strip-ansi": "^7.0.1", "through": "^2.3.6", - "wrap-ansi": "^8.0.1" + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/internal-slot": { @@ -4264,13 +4300,13 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "is-typed-array": "^1.1.10" }, "funding": { @@ -4348,9 +4384,9 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", + "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -4375,15 +4411,15 @@ } }, "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, "bin": { "is-docker": "cli.js" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4419,6 +4455,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-installed-globally": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", @@ -4587,12 +4641,12 @@ } }, "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4654,12 +4708,12 @@ "dev": true }, "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4698,6 +4752,21 @@ "node": ">=8" } }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-yarn-global": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", @@ -4719,6 +4788,22 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/issue-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", + "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", + "dev": true, + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": ">=10.13" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -4836,9 +4921,9 @@ } }, "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", + "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", "dev": true, "funding": { "type": "opencollective", @@ -4993,6 +5078,18 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true + }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -5005,23 +5102,41 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true + }, "node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5048,6 +5163,12 @@ "yallist": "^3.0.2" } }, + "node_modules/lru-cache/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -5088,9 +5209,9 @@ "dev": true }, "node_modules/marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -5233,58 +5354,6 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/mocha/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/mocha/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/mocha/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -5362,18 +5431,6 @@ "node": "*" } }, - "node_modules/mocha/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mocha/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5389,22 +5446,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mocha/node_modules/minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", @@ -5476,18 +5517,6 @@ "node": ">=8" } }, - "node_modules/mocha/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -5520,15 +5549,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/mocha/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/mocha/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -5547,15 +5567,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5563,10 +5574,13 @@ "dev": true }, "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/nanoid": { "version": "3.3.3", @@ -5641,6 +5655,15 @@ "path-to-regexp": "^1.7.0" } }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -5661,9 +5684,9 @@ } }, "node_modules/node-fetch": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", - "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", + "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", "dev": true, "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -5785,6 +5808,23 @@ "node": ">=8.9" } }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/nyc/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -5794,6 +5834,75 @@ "node": ">=8" } }, + "node_modules/nyc/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5864,17 +5973,18 @@ } }, "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", "dev": true, "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", "is-wsl": "^2.2.0" }, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5898,18 +6008,18 @@ } }, "node_modules/ora": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz", - "integrity": "sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.3.0.tgz", + "integrity": "sha512-1/D8uRFY0ay2kgBpmAwmSA404w4OoPVhHMqRqtjvrcK/dnzcEZxMJ+V4DUbyICu8IIVRclHcOf5wlD1tMY4GUQ==", "dev": true, "dependencies": { - "bl": "^5.0.0", "chalk": "^5.0.0", "cli-cursor": "^4.0.0", "cli-spinners": "^2.6.1", "is-interactive": "^2.0.0", "is-unicode-supported": "^1.1.0", "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", "strip-ansi": "^7.0.1", "wcwidth": "^1.0.1" }, @@ -5920,13 +6030,80 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dev": true, + "dependencies": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/os-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.0.1.tgz", - "integrity": "sha512-0EQpaHUHq7olp2/YFUr+0vZi9tMpDTblHGz+Ch5RntKxiRXOAY0JOz1UlxhSjMSksHvkm13eD6elJj3M8Ht/kw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.1.0.tgz", + "integrity": "sha512-YEIoAnM6zFmzw3PQ201gCVCIWbXNyKObGlVvpAVvraAeOHnlYVKFssbA/riRX5R40WA6kKrZ7Dr7dWzO3nKSeQ==", "dev": true, "dependencies": { - "macos-release": "^3.0.1", + "macos-release": "^3.1.0", "windows-release": "^5.0.1" }, "engines": { @@ -6082,9 +6259,9 @@ } }, "node_modules/package-json/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6096,12 +6273,6 @@ "node": ">=10" } }, - "node_modules/package-json/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6456,19 +6627,23 @@ } }, "node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6493,15 +6668,19 @@ "node": ">= 0.10" } }, + "node_modules/redis": { + "resolved": "packages/redis", + "link": true + }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -6510,25 +6689,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/registry-auth-token": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.1.tgz", - "integrity": "sha512-UfxVOj8seK1yaIOiieV4FIP01vfBDLsY0H9sQzi9EbbUdJiuuBjJgLa1DpImXMNPnVkBD4eVxTEXcrZA6kfpJA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", "dev": true, "dependencies": { - "@pnpm/npm-conf": "^1.0.4" + "@pnpm/npm-conf": "^2.1.0" }, "engines": { "node": ">=14" @@ -6550,29 +6717,30 @@ } }, "node_modules/release-it": { - "version": "15.6.0", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.6.0.tgz", - "integrity": "sha512-NXewgzO8QV1LOFjn2K7/dgE1Y1cG+2JiLOU/x9X/Lq9UdFn2hTH1r9SSrufCxG+y/Rp+oN8liYTsNptKrj92kg==", + "version": "15.10.1", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-15.10.1.tgz", + "integrity": "sha512-Wkk4aFHSo27vQwHIlcEy77lJwnQlh4UDQckc53gh5tKo7F22mAUEAe8SYQZJcFh7icdkf0OV70onhB1dDmeClA==", "dev": true, "dependencies": { "@iarna/toml": "2.2.5", - "@octokit/rest": "19.0.5", + "@octokit/rest": "19.0.7", "async-retry": "1.3.3", - "chalk": "5.1.2", - "cosmiconfig": "8.0.0", - "execa": "6.1.0", + "chalk": "5.2.0", + "cosmiconfig": "8.1.3", + "execa": "7.1.1", "git-url-parse": "13.1.0", - "globby": "13.1.2", - "got": "12.5.3", - "inquirer": "9.1.4", + "globby": "13.1.3", + "got": "12.6.0", + "inquirer": "9.1.5", "is-ci": "3.0.1", + "issue-parser": "6.0.0", "lodash": "4.17.21", "mime-types": "2.1.35", "new-github-release-url": "2.0.0", - "node-fetch": "3.3.0", - "open": "8.4.0", - "ora": "6.1.2", - "os-name": "5.0.1", + "node-fetch": "3.3.1", + "open": "9.1.0", + "ora": "6.3.0", + "os-name": "5.1.0", "promise.allsettled": "1.0.6", "proxy-agent": "5.0.0", "semver": "7.3.8", @@ -6589,10 +6757,22 @@ "node": ">=14.9" } }, + "node_modules/release-it/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/release-it/node_modules/globby": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", - "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", "dev": true, "dependencies": { "dir-glob": "^3.0.1", @@ -6647,11 +6827,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/release-it/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/release-it/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } }, "node_modules/release-zalgo": { "version": "1.0.0", @@ -6681,12 +6864,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -6801,42 +6984,134 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, "engines": { - "node": ">=0.12.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-applescript/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-applescript/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -6919,9 +7194,9 @@ } }, "node_modules/semver-diff/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6933,12 +7208,6 @@ "node": ">=10" } }, - "node_modules/semver-diff/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -7031,16 +7300,16 @@ "dev": true }, "node_modules/sinon": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", - "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.4.tgz", + "integrity": "sha512-uzmfN6zx3GQaria1kwgWGeKiXSSbShBbue6Dcj0SI8fiCNFbiUDqKl57WFlY5lyhxZVUKmXvzgG2pilRQCBwWg==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "10.0.2", - "@sinonjs/samsam": "^7.0.1", - "diff": "^5.0.0", - "nise": "^5.1.2", + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.4", "supports-color": "^7.2.0" }, "funding": { @@ -7048,6 +7317,15 @@ "url": "https://opencollective.com/sinon" } }, + "node_modules/sinon/node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7152,6 +7430,21 @@ "node": ">= 0.8" } }, + "node_modules/stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dev": true, + "dependencies": { + "bl": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", @@ -7165,13 +7458,10 @@ } }, "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true }, "node_modules/string-width": { "version": "5.1.2", @@ -7190,6 +7480,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", @@ -7219,18 +7553,15 @@ } }, "node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/strip-bom": { @@ -7278,15 +7609,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-outer/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7337,6 +7659,18 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -7397,15 +7731,6 @@ "node": ">=0.10.0" } }, - "node_modules/trim-repeated/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -7507,12 +7832,12 @@ } }, "node_modules/type-fest": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.6.0.tgz", - "integrity": "sha512-RqTRtKTzvPpNdDUp1dVkKQRunlPITk4mXeqFlAZoJsS+fLRn8AdPK0TcQDumGayhU7fjlBfiBjsq3pe3rIfXZQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">=14.16" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -7542,14 +7867,14 @@ } }, "node_modules/typedoc": { - "version": "0.23.25", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.25.tgz", - "integrity": "sha512-O1he153qVyoCgJYSvIyY3bPP1wAJTegZfa6tL3APinSZhJOf8CSd8F/21M6ex8pUY/fuY6n0jAsT4fIuMGA6sA==", + "version": "0.24.6", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.6.tgz", + "integrity": "sha512-c3y3h45xJv3qYwKDAwU6Cl+26CjT0ZvblHzfHJ+SjQDM4p1mZxtgHky4lhmG0+nNarRht8kADfZlbspJWdZarQ==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.2.12", - "minimatch": "^6.1.6", + "marked": "^4.3.0", + "minimatch": "^9.0.0", "shiki": "^0.14.1" }, "bin": { @@ -7559,7 +7884,7 @@ "node": ">= 14.14" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -7572,31 +7897,31 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-6.2.0.tgz", - "integrity": "sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", + "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=12.20" } }, "node_modules/unbox-primitive": { @@ -7653,10 +7978,19 @@ "node": ">= 0.8" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { @@ -7666,6 +8000,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -7673,7 +8011,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -7707,6 +8045,18 @@ "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/update-notifier/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -7720,9 +8070,9 @@ } }, "node_modules/update-notifier/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -7734,12 +8084,6 @@ "node": ">=10" } }, - "node_modules/update-notifier/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -7780,9 +8124,9 @@ "dev": true }, "node_modules/vm2": { - "version": "3.9.14", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", - "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", + "version": "3.9.17", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.17.tgz", + "integrity": "sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==", "dev": true, "dependencies": { "acorn": "^8.7.0", @@ -7873,9 +8217,9 @@ } }, "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "dev": true }, "node_modules/which-typed-array": { @@ -7966,18 +8310,6 @@ "node": ">=10.17.0" } }, - "node_modules/windows-release/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/windows-release/node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -8055,6 +8387,45 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -8095,46 +8466,44 @@ } }, "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } }, "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dev": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/yargs-unparser": { @@ -8176,15 +8545,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -8205,29 +8565,13 @@ "node": ">=8" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yargs/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, "engines": { - "node": ">=6" + "node": ">=12" } }, "node_modules/yn": { @@ -8258,13 +8602,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.16.1", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8282,29 +8626,24 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", - "@types/sinon": "^10.0.13", + "@types/node": "^18.16.1", + "@types/sinon": "^10.0.14", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.53.0", - "@typescript-eslint/parser": "^5.53.0", - "eslint": "^8.34.0", + "@typescript-eslint/eslint-plugin": "^5.59.1", + "@typescript-eslint/parser": "^5.59.1", + "eslint": "^8.39.0", "nyc": "^15.1.0", - "release-it": "^15.6.0", - "sinon": "^15.0.1", + "release-it": "^15.10.1", + "sinon": "^15.0.4", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "engines": { "node": ">=14" } }, - "packages/client/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "packages/graph": { "name": "@redis/graph", "version": "1.1.0", @@ -8312,13 +8651,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.16.1", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8331,18 +8670,34 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.16.1", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, + "packages/redis": { + "version": "4.6.6", + "license": "MIT", + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.7", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.2", + "@redis/time-series": "1.0.4" + }, + "devDependencies": { + "release-it": "^15.9.3", + "typescript": "^5.0.2" + } + }, "packages/search": { "name": "@redis/search", "version": "1.1.2", @@ -8350,13 +8705,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.16.1", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "peerDependencies": { "@redis/client": "^1.0.0" @@ -8367,133 +8722,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^18.14.1", - "@types/yargs": "^17.0.22", + "@types/node": "^18.16.1", + "@types/yargs": "^17.0.24", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.9.5", + "typescript": "^5.0.4", "yargs": "^17.7.1" }, "peerDependencies": { "@redis/client": "^1.0.0" } }, - "packages/test-utils/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "packages/test-utils/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/test-utils/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "packages/test-utils/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/test-utils/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "packages/test-utils/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "packages/test-utils/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, "packages/time-series": { "name": "@redis/time-series", "version": "1.0.4", @@ -8501,13 +8742,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.16.1", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "peerDependencies": { "@redis/client": "^1.0.0" diff --git a/package.json b/package.json index 423c8506648..afb549a486c 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,6 @@ { - "name": "redis", - "description": "A modern, high performance Redis client", - "version": "4.6.6", - "license": "MIT", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "files": [ - "dist/" - ], + "name": "redis-monorepo", + "private": true, "workspaces": [ "./packages/*" ], @@ -15,36 +8,14 @@ "test": "npm run test -ws --if-present", "build:client": "npm run build -w ./packages/client", "build:test-utils": "npm run build -w ./packages/test-utils", - "build:tests-tools": "npm run build:client && npm run build:test-utils", - "build:modules": "find ./packages -mindepth 1 -maxdepth 1 -type d ! -name 'client' ! -name 'test-utils' -exec npm run build -w {} \\;", - "build": "tsc", - "build-all": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build", + "build:modules": "npm run build -w ./packages/bloom -w ./packages/graph -w ./packages/json -w ./packages/search -w ./packages/time-series", + "build:redis": "npm run build -w ./packages/redis", + "build": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build:redis", "documentation": "npm run documentation -ws --if-present", "gh-pages": "gh-pages -d ./documentation -e ./documentation -u 'documentation-bot '" }, - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.5.7", - "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.2", - "@redis/time-series": "1.0.4" - }, "devDependencies": { - "@tsconfig/node14": "^1.0.3", - "gh-pages": "^5.0.0", - "release-it": "^15.6.0", - "typescript": "^4.9.5" - }, - "repository": { - "type": "git", - "url": "git://github.com/redis/node-redis.git" - }, - "bugs": { - "url": "https://github.com/redis/node-redis/issues" - }, - "homepage": "https://github.com/redis/node-redis", - "keywords": [ - "redis" - ] + "@tsconfig/node16": "^1.0.3", + "gh-pages": "^5.0.0" + } } diff --git a/packages/bloom/lib/commands/bloom/INSERT.ts b/packages/bloom/lib/commands/bloom/INSERT.ts index f6deb7a8612..4c3cec0f79b 100644 --- a/packages/bloom/lib/commands/bloom/INSERT.ts +++ b/packages/bloom/lib/commands/bloom/INSERT.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const FIRST_KEY_INDEX = 1; @@ -39,7 +39,7 @@ export function transformArguments( } args.push('ITEMS'); - return pushVerdictArguments(args, items); + return pushVariadicArguments(args, items); } export { transformBooleanArrayReply as transformReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts index 13a71c9b1de..a34a9e9b9a0 100644 --- a/packages/bloom/lib/commands/count-min-sketch/QUERY.ts +++ b/packages/bloom/lib/commands/count-min-sketch/QUERY.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,7 +9,7 @@ export function transformArguments( key: string, items: string | Array ): RedisCommandArguments { - return pushVerdictArguments(['CMS.QUERY', key], items); + return pushVariadicArguments(['CMS.QUERY', key], items); } export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/cuckoo/index.ts b/packages/bloom/lib/commands/cuckoo/index.ts index 96b4453bc39..c34bdde503d 100644 --- a/packages/bloom/lib/commands/cuckoo/index.ts +++ b/packages/bloom/lib/commands/cuckoo/index.ts @@ -10,7 +10,7 @@ import * as INSERTNX from './INSERTNX'; import * as LOADCHUNK from './LOADCHUNK'; import * as RESERVE from './RESERVE'; import * as SCANDUMP from './SCANDUMP'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; export default { @@ -58,5 +58,5 @@ export function pushInsertOptions( } args.push('ITEMS'); - return pushVerdictArguments(args, items); + return pushVariadicArguments(args, items); } diff --git a/packages/bloom/lib/commands/t-digest/MERGE.ts b/packages/bloom/lib/commands/t-digest/MERGE.ts index 5119d0b9e18..1aebe6fa47b 100644 --- a/packages/bloom/lib/commands/t-digest/MERGE.ts +++ b/packages/bloom/lib/commands/t-digest/MERGE.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { CompressionOption, pushCompressionArgument } from '.'; export const FIRST_KEY_INDEX = 1; @@ -13,7 +13,7 @@ export function transformArguments( srcKeys: RedisCommandArgument | Array, options?: MergeOptions ): RedisCommandArguments { - const args = pushVerdictArgument( + const args = pushVariadicArgument( ['TDIGEST.MERGE', destKey], srcKeys ); diff --git a/packages/bloom/lib/commands/top-k/ADD.ts b/packages/bloom/lib/commands/top-k/ADD.ts index beee3a2206c..125c2e71bef 100644 --- a/packages/bloom/lib/commands/top-k/ADD.ts +++ b/packages/bloom/lib/commands/top-k/ADD.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -7,7 +7,7 @@ export function transformArguments( key: string, items: string | Array ): RedisCommandArguments { - return pushVerdictArguments(['TOPK.ADD', key], items); + return pushVariadicArguments(['TOPK.ADD', key], items); } export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/top-k/COUNT.ts b/packages/bloom/lib/commands/top-k/COUNT.ts index fc8cf557dca..cb877ecc25f 100644 --- a/packages/bloom/lib/commands/top-k/COUNT.ts +++ b/packages/bloom/lib/commands/top-k/COUNT.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,7 +9,7 @@ export function transformArguments( key: string, items: string | Array ): RedisCommandArguments { - return pushVerdictArguments(['TOPK.COUNT', key], items); + return pushVariadicArguments(['TOPK.COUNT', key], items); } export declare function transformReply(): Array; diff --git a/packages/bloom/lib/commands/top-k/QUERY.ts b/packages/bloom/lib/commands/top-k/QUERY.ts index 94943a26fd7..55451df3d45 100644 --- a/packages/bloom/lib/commands/top-k/QUERY.ts +++ b/packages/bloom/lib/commands/top-k/QUERY.ts @@ -1,5 +1,5 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -9,7 +9,7 @@ export function transformArguments( key: string, items: string | Array ): RedisCommandArguments { - return pushVerdictArguments(['TOPK.QUERY', key], items); + return pushVariadicArguments(['TOPK.QUERY', key], items); } export declare function transformReply(): Array; diff --git a/packages/bloom/package.json b/packages/bloom/package.json index d2e2c80806d..9bcae785dd9 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.16.1", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "repository": { "type": "git", diff --git a/packages/client/.eslintrc.json b/packages/client/.eslintrc.json index 4536bc31338..3037932fd97 100644 --- a/packages/client/.eslintrc.json +++ b/packages/client/.eslintrc.json @@ -1,15 +1,15 @@ { - "root": true, - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" - ], - "rules": { - "semi": [2, "always"] - } + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "semi": [2, "always"] } +} diff --git a/packages/client/index.ts b/packages/client/index.ts index 8b21c5d5a32..c9b93f3377b 100644 --- a/packages/client/index.ts +++ b/packages/client/index.ts @@ -1,24 +1,19 @@ -import RedisClient from './lib/client'; -import RedisCluster from './lib/cluster'; - -export { RedisClientType, RedisClientOptions } from './lib/client'; - -export { RedisModules, RedisFunctions, RedisScripts } from './lib/commands'; +export { RedisModules, RedisFunctions, RedisScripts, RespVersions } from './lib/RESP/types'; +export { RESP_TYPES } from './lib/RESP/decoder'; +export { VerbatimString } from './lib/RESP/verbatim-string'; +export { defineScript } from './lib/lua-script'; +// export * from './lib/errors'; +import RedisClient, { RedisClientType, RedisClientOptions } from './lib/client'; +export { RedisClientType, RedisClientOptions }; export const createClient = RedisClient.create; -export const commandOptions = RedisClient.commandOptions; - -export { RedisClusterType, RedisClusterOptions } from './lib/cluster'; - +import RedisCluster, { RedisClusterType, RedisClusterOptions } from './lib/cluster'; +export { RedisClusterType, RedisClusterOptions }; export const createCluster = RedisCluster.create; -export { defineScript } from './lib/lua-script'; - -export * from './lib/errors'; - -export { GeoReplyWith } from './lib/commands/generic-transformers'; +// export { GeoReplyWith } from './lib/commands/generic-transformers'; -export { SetOptions } from './lib/commands/SET'; +// export { SetOptions } from './lib/commands/SET'; -export { RedisFlushModes } from './lib/commands/FLUSHALL'; +// export { RedisFlushModes } from './lib/commands/FLUSHALL'; diff --git a/packages/client/lib/RESP/decoder-ts.ts b/packages/client/lib/RESP/decoder-ts.ts new file mode 100644 index 00000000000..0586940f692 --- /dev/null +++ b/packages/client/lib/RESP/decoder-ts.ts @@ -0,0 +1,1043 @@ +// import { ErrorReply } from '../../errors'; +// import { Flags, ReplyUnion, RespTypesUnion, ReplyWithFlags, RespTypes, Flag } from './types'; + +// // https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md +// export const TYPES = { +// NULL: 95, // _ +// BOOLEAN: 35, // # +// NUMBER: 58, // : +// BIG_NUMBER: 40, // ( +// DOUBLE: 44, // , +// SIMPLE_STRING: 43, // + +// BLOB_STRING: 36, // $ +// VERBATIM_STRING: 61, // = +// SIMPLE_ERROR: 45, // - +// BLOB_ERROR: 33, // ! +// ARRAY: 42, // * +// SET: 126, // ~ +// MAP: 37, // % +// PUSH: 62 // > +// } as const; + +// const ASCII = { +// '\r': 13, +// 't': 116, +// '-': 45, +// '0': 48, +// '.': 46, +// 'i': 105, +// 'n': 110 +// } as const; + +// // this was written with performance in mind, so it's not very readable... sorry :( + +// interface DecoderConfig { +// // TODO: on parser error? +// // TODO: types +// onReply(reply: unknown): unknown; +// onErrorReply(reply: unknown): unknown; +// onPush(push: unknown): unknown; +// getFlags(): Flags; +// } + +// type ValueCb = DecoderConfig['onReply'] | DecoderConfig['onErrorReply'] | DecoderConfig['onPush']; + +// type Next = (chunk: Buffer) => ValueOrNext; + +// type ValueOrNext = T | Next; + +// export class Decoder { +// #config: DecoderConfig; + +// #cursor = 0; + +// #next: Next | undefined; + +// constructor(config: DecoderConfig) { +// this.#config = config; +// } + +// reset() { +// this.#cursor = 0; +// this.#next = undefined; +// } + +// write(chunk: Buffer) { +// if (this.#cursor >= chunk.length) { +// this.#cursor -= chunk.length; +// return; +// } + +// if (this.#next) { +// if (this.#next(chunk) || this.#cursor >= chunk.length) { +// this.#cursor -= chunk.length; +// return; +// } +// } + +// do { +// const flags = this.#config.getFlags(), +// type = chunk[this.#cursor] as RespTypesUnion; + +// if (++this.#cursor === chunk.length) { +// this.#next = this.#continueDecodeTypeValue.bind(this, type, flags); +// break; +// } + +// if (this.#decodeTypeValue(type, flags, chunk)) { +// break; +// } +// } while (this.#cursor < chunk.length); +// this.#cursor -= chunk.length; +// } + +// #continueDecodeTypeValue( +// type: RespTypesUnion, +// flags: Flags, +// chunk: Buffer +// ) { +// this.#next = undefined; +// return this.#decodeTypeValue(type, flags, chunk); +// } + +// #decodeTypeValue( +// type: RespTypesUnion, +// flags: Flags, +// chunk: Buffer +// ) { +// switch (type) { +// case TYPES.NULL: +// this.#config.onReply(this.#decodeNull()); +// return false; + +// case TYPES.BOOLEAN: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeBoolean(chunk) +// ); + +// case TYPES.NUMBER: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeNumber(chunk) +// ); + +// case TYPES.BIG_NUMBER: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeBigNumber(flags[TYPES.BIG_NUMBER], chunk) +// ); + +// case TYPES.DOUBLE: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeDouble(flags[TYPES.DOUBLE], chunk) +// ); + +// case TYPES.SIMPLE_STRING: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeDouble(flags[TYPES.DOUBLE], chunk) +// ); + +// case TYPES.BLOB_STRING: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeBlobString(flags[TYPES.BLOB_STRING], chunk) +// ); + +// case TYPES.VERBATIM_STRING: +// throw new Error('TODO: verbatim string'); + +// case TYPES.SIMPLE_ERROR: +// return this.#handleDecodedValue( +// this.#config.onErrorReply, +// this.#decodeSimpleError(chunk) +// ); + +// case TYPES.BLOB_ERROR: +// return this.#handleDecodedValue( +// this.#config.onErrorReply, +// this.#decodeBlobError(chunk) +// ); + +// case TYPES.ARRAY: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeArray(flags, chunk) +// ); + +// case TYPES.SET: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeSet(flags, chunk) +// ); + +// case TYPES.MAP: +// return this.#handleDecodedValue( +// this.#config.onReply, +// this.#decodeMap(flags, chunk) +// ); + +// case TYPES.PUSH: +// return this.#handleDecodedValue( +// this.#config.onPush, +// this.#decodeArray(flags, chunk) +// ); +// } +// } + +// #handleDecodedValue(cb: ValueCb, value: ValueOrNext) { +// if (typeof value === 'function') { +// this.#next = this.#continueDecodeValue.bind(this, cb, value); +// return true; +// } + +// cb(value); +// return false; +// } + +// #continueDecodeValue( +// cb: ValueCb, +// next: Next, +// chunk: Buffer +// ) { +// this.#next = undefined; +// return this.#handleDecodedValue(cb, next(chunk)); +// } + +// #decodeNull() { +// this.#cursor += 2; // skip \r\n +// return null; +// } + +// #decodeBoolean(chunk: Buffer) { +// const boolean = chunk[this.#cursor] === ASCII['t']; +// this.#cursor += 3; // skip {t | f}\r\n +// return boolean; +// } + +// #decodeNumber(chunk: Buffer) { +// const isNegative = chunk[this.#cursor] === ASCII['-']; +// if (isNegative && ++this.#cursor === chunk.length) { +// return this.#continueDecodeNumber.bind( +// this, +// isNegative, +// this.#decodeUnsingedNumber.bind(this, 0) +// ); +// } + +// const number = this.#decodeUnsingedNumber(0, chunk); +// return typeof number === 'function' ? +// this.#continueDecodeNumber.bind(this, isNegative, number) : +// isNegative ? -number : number; +// } + +// #continueDecodeNumber( +// isNegative: boolean, +// numberCb: Next, +// chunk: Buffer +// ): ValueOrNext { +// const number = numberCb(chunk); +// return typeof number === 'function' ? +// this.#continueDecodeNumber.bind(this, isNegative, number) : +// isNegative ? -number : number; +// } + +// #decodeUnsingedNumber( +// number: number, +// chunk: Buffer +// ): ValueOrNext { +// let cursor = this.#cursor; +// do { +// const byte = chunk[cursor]; +// if (byte === ASCII['\r']) { +// this.#cursor = cursor + 2; // skip \r\n +// return number; +// } +// number = number * 10 + byte - ASCII['0']; +// } while (++cursor < chunk.length); + +// this.#cursor = cursor; +// return this.#decodeUnsingedNumber.bind(this, number); +// } + +// #decodeBigNumber(flag: Flags[typeof TYPES.BIG_NUMBER], chunk: Buffer) { +// if (flag === String) { +// return this.#decodeSimpleString(String, chunk); +// } + +// const isNegative = chunk[this.#cursor] === ASCII['-']; +// if (isNegative && ++this.#cursor === chunk.length) { +// return this.#continueDecodeBigNumber.bind( +// this, +// isNegative, +// this.#decodeUnsingedBigNumber.bind(this, 0n) +// ); +// } + +// const bigNumber = this.#decodeUnsingedBigNumber(0n, chunk); +// return typeof bigNumber === 'function' ? +// this.#continueDecodeBigNumber.bind(this, isNegative, bigNumber) : +// isNegative ? -bigNumber : bigNumber; +// } + +// #continueDecodeBigNumber( +// isNegative: boolean, +// bigNumberCb: Next, +// chunk: Buffer +// ): ValueOrNext { +// const bigNumber = bigNumberCb(chunk); +// return typeof bigNumber === 'function' ? +// this.#continueDecodeBigNumber.bind(this, isNegative, bigNumber) : +// isNegative ? -bigNumber : bigNumber; +// } + +// #decodeUnsingedBigNumber( +// bigNumber: bigint, +// chunk: Buffer +// ): ValueOrNext { +// let cursor = this.#cursor; +// do { +// const byte = chunk[cursor]; +// if (byte === ASCII['\r']) { +// this.#cursor = cursor + 2; // skip \r\n +// return bigNumber; +// } +// bigNumber = bigNumber * 10n + BigInt(byte - ASCII['0']); +// } while (++cursor < chunk.length); + +// this.#cursor = cursor; +// return this.#decodeUnsingedBigNumber.bind(this, bigNumber); +// } + +// #decodeDouble(flag: Flags[RespTypes['DOUBLE']], chunk: Buffer) { +// if (flag === String) { +// return this.#decodeSimpleString(String, chunk); +// } + +// switch (chunk[this.#cursor]) { +// case ASCII['n']: +// this.#cursor += 5; // skip nan\r\n +// return NaN; + +// case ASCII['-']: +// return ++this.#cursor === chunk.length ? +// this.#decodeDoubleInteger.bind(this, true, 0, chunk) : +// this.#decodeDoubleInteger(true, 0, chunk); + +// default: +// return this.#decodeDoubleInteger(false, 0, chunk); +// } +// } + +// #decodeDoubleInteger( +// isNegative: boolean, +// integer: number, +// chunk: Buffer +// ) { +// if (chunk[this.#cursor] === ASCII['i']) { +// this.#cursor += 5; // skip inf\r\n +// return isNegative ? -Infinity : Infinity; +// } + +// return this.#continueDecodeDoubleInteger(isNegative, integer, chunk); +// } + +// #continueDecodeDoubleInteger( +// isNegative: boolean, +// integer: number, +// chunk: Buffer +// ): ValueOrNext { +// let cursor = this.#cursor; +// do { +// const byte = chunk[cursor]; +// switch (byte) { +// case ASCII['.']: +// this.#cursor = ++cursor; +// return cursor < chunk.length ? +// this.#decodeDoubleDecimal(isNegative, 0, integer, chunk) : +// this.#decodeDoubleDecimal.bind(this, isNegative, 0, integer); + +// case ASCII['\r']: +// this.#cursor = cursor + 2; // skip \r\n +// return isNegative ? -integer : integer; + +// default: +// integer = integer * 10 + byte - ASCII['0']; +// } +// } while (++cursor < chunk.length); + +// this.#cursor = cursor; +// return this.#continueDecodeDoubleInteger.bind(this, isNegative, integer); +// } + +// // Precalculated multipliers for decimal points to improve performance +// // "A Number only keeps about 17 decimal places of precision" +// // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number +// static #DOUBLE_DECIMAL_MULTIPLIERS = [ +// 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, +// 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, +// 1e-13, 1e-14, 1e-15, 1e-16, 1e-17 +// ]; + +// #decodeDoubleDecimal( +// isNegative: boolean, +// decimalIndex: number, +// double: number, +// chunk: Buffer +// ): ValueOrNext { +// let cursor = this.#cursor; +// do { +// const byte = chunk[cursor]; +// if (byte === ASCII['\r']) { +// this.#cursor = cursor + 2; // skip \r\n +// return isNegative ? -double : double; +// } + +// if (decimalIndex < Decoder.#DOUBLE_DECIMAL_MULTIPLIERS.length) { +// double += (byte - ASCII['0']) * Decoder.#DOUBLE_DECIMAL_MULTIPLIERS[decimalIndex++]; +// } +// } while (++cursor < chunk.length); + +// this.#cursor = cursor; +// return this.#decodeDoubleDecimal.bind(this, isNegative, decimalIndex, double); +// } + +// #findCRLF(chunk: Buffer, cursor: number) { +// while (chunk[cursor] !== ASCII['\r']) { +// if (++cursor === chunk.length) { +// this.#cursor = chunk.length; +// return -1; +// } +// } + +// this.#cursor = cursor + 2; // skip \r\n +// return cursor; +// } + +// #decodeSimpleString( +// flag: F, +// chunk: Buffer +// ): ValueOrNext ? T : never> { +// const start = this.#cursor, +// crlfIndex = this.#findCRLF(chunk, start); +// if (crlfIndex === -1) { +// return this.#continueDecodeSimpleString.bind( +// this, +// [chunk.subarray(start)], +// flag +// ); +// } + +// const slice = chunk.subarray(start, crlfIndex); +// return (flag === Buffer ? +// slice : +// slice.toString()) as (F extends Flag ? T : never); +// } + +// #continueDecodeSimpleString( +// chunks: Array, +// flag: F, +// chunk: Buffer +// ): ValueOrNext ? T : never> { +// const start = this.#cursor, +// crlfIndex = this.#findCRLF(chunk, start); +// if (crlfIndex === -1) { +// chunks.push(chunk.subarray(start)); +// return this.#continueDecodeSimpleString.bind(this, chunks, flag) as Next ? T : never>; +// } + +// chunks.push(chunk.subarray(start, crlfIndex)); +// return (flag === Buffer ? +// Buffer.concat(chunks) : +// chunks.join('')) as ValueOrNext ? T : never>; +// } + +// #decodeBlobString(flag: Flags[RespTypes['BLOB_STRING']], chunk: Buffer) { +// // RESP 2 bulk string null +// // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-bulk-strings +// if (chunk[this.#cursor] === ASCII['-']) { +// this.#cursor += 4; // skip -1\r\n +// return null; +// } + +// const length = this.#decodeUnsingedNumber(0, chunk); +// if (typeof length === 'function') { +// return this.#continueDecodeBlobStringLength.bind(this, length, flag); +// } else if (this.#cursor >= chunk.length) { +// return this.#decodeBlobStringWithLength.bind(this, length, flag); +// } + +// return this.#decodeBlobStringWithLength(length, flag, chunk); +// } + +// #continueDecodeBlobStringLength( +// lengthCb: Next, +// flag: Flags[RespTypes['BLOB_STRING']], +// chunk: Buffer +// ): ValueOrNext { +// const length = lengthCb(chunk); +// if (typeof length === 'function') { +// return this.#continueDecodeBlobStringLength.bind(this, length, flag); +// } else if (this.#cursor >= chunk.length) { +// return this.#decodeBlobStringWithLength.bind(this, length, flag); +// } + +// return this.#decodeBlobStringWithLength(length, flag, chunk); +// } + +// #decodeBlobStringWithLength( +// length: number, +// flag: Flags[RespTypes['BLOB_STRING']], +// chunk: Buffer +// ) { +// const end = this.#cursor + length; +// if (end >= chunk.length) { +// const slice = chunk.subarray(this.#cursor); +// this.#cursor = chunk.length; +// return this.#continueDecodeBlobStringWithLength.bind( +// this, +// length - slice.length, +// [slice], +// flag +// ); +// } + +// const slice = chunk.subarray(this.#cursor, end); +// this.#cursor = end + 2; // skip ${string}\r\n +// return flag === Buffer ? +// slice : +// slice.toString(); +// } + +// #continueDecodeBlobStringWithLength( +// length: number, +// chunks: Array, +// flag: Flags[RespTypes['BLOB_STRING']], +// chunk: Buffer +// ): ValueOrNext { +// const end = this.#cursor + length; +// if (end >= chunk.length) { +// const slice = chunk.subarray(this.#cursor); +// chunks.push(slice); +// this.#cursor = chunk.length; +// return this.#continueDecodeBlobStringWithLength.bind( +// this, +// length - slice.length, +// chunks, +// flag +// ); +// } + +// chunks.push(chunk.subarray(this.#cursor, end)); +// this.#cursor = end + 2; // skip ${string}\r\n +// return flag === Buffer ? +// Buffer.concat(chunks) : +// chunks.join(''); +// } + +// #decodeSimpleError(chunk: Buffer) { +// const string = this.#decodeSimpleString(String, chunk); +// return typeof string === 'function' ? +// this.#continueDecodeSimpleError.bind(this, string) : +// new Error(string); // TODO use custom error +// } + +// #continueDecodeSimpleError(stringCb, chunk) { +// const string = stringCb(chunk); +// return typeof string === 'function' ? +// this.#continueDecodeSimpleError.bind(this, string) : +// new Error(string); // TODO use custom error +// } + +// #decodeBlobError(chunk) { +// const string = this.#decodeBlobString(String, chunk); +// return typeof string === 'function' ? +// this.#continueDecodeBlobError.bind(this, string) : +// new Error(string); // TODO use custom error +// } + +// #continueDecodeBlobError(stringCb, chunk) { +// const string = stringCb(chunk); +// return typeof string === 'function' ? +// this.#continueDecodeBlobError.bind(this, string) : +// new Error(string); // TODO use custom error +// } + +// #decodeNestedType(flags, chunk) { +// const type = chunk[this.#cursor]; +// return ++this.#cursor === chunk.length ? +// this.#decodeReplyValue.bind(this, type, flags) : +// this.#decodeReplyValue(type, flags, chunk); +// } + +// #decodeArray(flags, chunk) { +// // RESP 2 null +// // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-arrays +// if (chunk[this.#cursor] === ASCII['-']) { +// this.#cursor += 4; // skip -1\r\n +// return null; +// } + +// return this.#decodeArrayWithLength( +// this.#decodeUnsingedNumber(0, chunk), +// flags, +// chunk +// ); +// } + +// #decodeArrayWithLength(length, flags, chunk) { +// return typeof length === 'function' ? +// this.#continueDecodeArrayLength.bind(this, length, flags) : +// this.#decodeArrayItems( +// new Array(length), +// 0, +// flags, +// chunk +// ); +// } + +// #continueDecodeArrayLength(lengthCb, flags, chunk) { +// return this.#decodeArrayWithLength( +// lengthCb(chunk), +// flags, +// chunk +// ); +// } + +// #decodeArrayItems(array, filled, flags, chunk) { +// for (let i = filled; i < array.length; i++) { +// if (this.#cursor >= chunk.length) { +// return this.#decodeArrayItems.bind( +// this, +// array, +// i, +// flags +// ); +// } + +// const item = this.#decodeNestedType(flags, chunk); +// if (typeof item === 'function') { +// return this.#continueDecodeArrayItems.bind( +// this, +// array, +// i, +// item, +// flags +// ); +// } + +// array[i] = item; +// } + +// return array; +// } + +// #continueDecodeArrayItems(array, filled, itemCb, flags, chunk) { +// const item = itemCb(chunk); +// if (typeof item === 'function') { +// return this.#continueDecodeArrayItems.bind( +// this, +// array, +// filled, +// item, +// flags +// ); +// } + +// array[filled++] = item; + +// return this.#decodeArrayItems(array, filled, flags, chunk); +// } + +// #decodeSet(flags, chunk) { +// const length = this.#decodeUnsingedNumber(0, chunk); +// if (typeof length === 'function') { +// return this.#continueDecodeSetLength.bind(this, length, flags); +// } + +// return this.#decodeSetItems( +// length, +// flags, +// chunk +// ); +// } + +// #continueDecodeSetLength(lengthCb, flags, chunk) { +// const length = lengthCb(chunk); +// return typeof length === 'function' ? +// this.#continueDecodeSetLength.bind(this, length, flags) : +// this.#decodeSetItems(length, flags, chunk); +// } + +// #decodeSetItems(length, flags, chunk) { +// return flags[TYPES.SET] === Set ? +// this.#decodeSetAsSet( +// new Set(), +// length, +// flags, +// chunk +// ) : +// this.#decodeArrayItems( +// new Array(length), +// 0, +// flags, +// chunk +// ); +// } + +// #decodeSetAsSet(set, remaining, flags, chunk) { +// // using `remaining` instead of `length` & `set.size` to make it work even if the set contains duplicates +// while (remaining > 0) { +// if (this.#cursor >= chunk.length) { +// return this.#decodeSetAsSet.bind( +// this, +// set, +// remaining, +// flags +// ); +// } + +// const item = this.#decodeNestedType(flags, chunk); +// if (typeof item === 'function') { +// return this.#continueDecodeSetAsSet.bind( +// this, +// set, +// remaining, +// item, +// flags +// ); +// } + +// set.add(item); +// --remaining; +// } + +// return set; +// } + +// #continueDecodeSetAsSet(set, remaining, itemCb, flags, chunk) { +// const item = itemCb(chunk); +// if (typeof item === 'function') { +// return this.#continueDecodeSetAsSet.bind( +// this, +// set, +// remaining, +// item, +// flags +// ); +// } + +// set.add(item); + +// return this.#decodeSetAsSet(set, remaining - 1, flags, chunk); +// } + +// #decodeMap(flags, chunk) { +// const length = this.#decodeUnsingedNumber(0, chunk); +// if (typeof length === 'function') { +// return this.#continueDecodeMapLength.bind(this, length, flags); +// } + +// return this.#decodeMapItems( +// length, +// flags, +// chunk +// ); +// } + +// #continueDecodeMapLength(lengthCb, flags, chunk) { +// const length = lengthCb(chunk); +// return typeof length === 'function' ? +// this.#continueDecodeMapLength.bind(this, length, flags) : +// this.#decodeMapItems(length, flags, chunk); +// } + +// #decodeMapItems(length, flags, chunk) { +// switch (flags[TYPES.MAP]) { +// case Map: +// return this.#decodeMapAsMap( +// new Map(), +// length, +// flags, +// chunk +// ); + +// case Array: +// return this.#decodeArrayItems( +// new Array(length * 2), +// 0, +// flags, +// chunk +// ); + +// default: +// return this.#decodeMapAsObject( +// Object.create(null), +// length, +// flags, +// chunk +// ); +// } +// } + +// #decodeMapAsMap(map, remaining, flags, chunk) { +// // using `remaining` instead of `length` & `map.size` to make it work even if the map contains duplicate keys +// while (remaining > 0) { +// if (this.#cursor >= chunk.length) { +// return this.#decodeMapAsMap.bind( +// this, +// map, +// remaining, +// flags +// ); +// } + +// const key = this.#decodeMapKey(flags, chunk); +// if (typeof key === 'function') { +// return this.#continueDecodeMapKey.bind( +// this, +// map, +// remaining, +// key, +// flags +// ); +// } + +// if (this.#cursor >= chunk.length) { +// return this.#continueDecodeMapValue.bind( +// this, +// map, +// remaining, +// key, +// this.#decodeNestedType.bind(this, flags), +// flags +// ); +// } + +// const value = this.#decodeNestedType(flags, chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapValue.bind( +// this, +// map, +// remaining, +// key, +// value, +// flags +// ); +// } + +// map.set(key, value); +// --remaining; +// } + +// return map; +// } + +// #decodeMapKey(flags, chunk) { +// const type = chunk[this.#cursor]; +// return ++this.#cursor === chunk.length ? +// this.#decodeMapKeyValue.bind(this, type, flags) : +// this.#decodeMapKeyValue(type, flags, chunk); +// } + +// #decodeMapKeyValue(type, flags, chunk) { +// switch (type) { +// // decode simple string map key as string (and not as buffer) +// case TYPES.SIMPLE_STRING: +// return this.#decodeSimpleString(String, chunk); + +// // decode blob string map key as string (and not as buffer) +// case TYPES.BLOB_STRING: +// return this.#decodeBlobString(String, chunk); + +// default: +// return this.#decodeReplyValue(type, flags, chunk); +// } +// } + +// #continueDecodeMapKey(map, remaining, keyCb, flags, chunk) { +// const key = keyCb(chunk); +// if (typeof key === 'function') { +// return this.#continueDecodeMapKey.bind( +// this, +// map, +// remaining, +// key, +// flags +// ); +// } + +// if (this.#cursor >= chunk.length) { +// return this.#continueDecodeMapValue.bind( +// this, +// map, +// remaining, +// key, +// this.#decodeNestedType.bind(this, flags), +// flags +// ); +// } + +// const value = this.#decodeNestedType(flags, chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapValue.bind( +// this, +// map, +// remaining, +// key, +// value, +// flags +// ); +// } + +// map.set(key, value); +// return this.#decodeMapAsMap(map, remaining - 1, flags, chunk); +// } + +// #continueDecodeMapValue(map, remaining, key, valueCb, flags, chunk) { +// const value = valueCb(chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapValue.bind( +// this, +// map, +// remaining, +// key, +// value, +// flags +// ); +// } + +// map.set(key, value); + +// return this.#decodeMapAsMap(map, remaining - 1, flags, chunk); +// } + +// #decodeMapAsObject(object, remaining, flags, chunk) { +// while (remaining > 0) { +// if (this.#cursor >= chunk.length) { +// return this.#decodeMapAsObject.bind( +// this, +// object, +// remaining, +// flags +// ); +// } + +// const key = this.#decodeMapKey(flags, chunk); +// if (typeof key === 'function') { +// return this.#continueDecodeMapAsObjectKey.bind( +// this, +// object, +// remaining, +// key, +// flags +// ); +// } + +// if (this.#cursor >= chunk.length) { +// return this.#continueDecodeMapAsObjectValue.bind( +// this, +// object, +// remaining, +// key, +// this.#decodeNestedType.bind(this, flags), +// flags +// ); +// } + +// const value = this.#decodeNestedType(flags, chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapAsObjectValue.bind( +// this, +// object, +// remaining, +// key, +// value, +// flags +// ); +// } + +// object[key] = value; +// --remaining; +// } + +// return object; +// } + +// #continueDecodeMapAsObjectKey(object, remaining, keyCb, flags, chunk) { +// const key = keyCb(chunk); +// if (typeof key === 'function') { +// return this.#continueDecodeMapAsObjectKey.bind( +// this, +// object, +// remaining, +// key, +// flags +// ); +// } + +// if (this.#cursor >= chunk.length) { +// return this.#continueDecodeMapAsObjectValue.bind( +// this, +// object, +// remaining, +// key, +// this.#decodeNestedType.bind(this, flags), +// flags +// ); +// } + +// const value = this.#decodeNestedType(flags, chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapAsObjectValue.bind( +// this, +// object, +// remaining, +// key, +// value, +// flags +// ); +// } + +// object[key] = value; + +// return this.#decodeMapAsObject(object, remaining - 1, flags, chunk); +// } + +// #continueDecodeMapAsObjectValue(object, remaining, key, valueCb, flags, chunk) { +// const value = valueCb(chunk); +// if (typeof value === 'function') { +// return this.#continueDecodeMapAsObjectValue.bind( +// this, +// object, +// remaining, +// key, +// value, +// flags +// ); +// } + +// object[key] = value; + +// return this.#decodeMapAsObject(object, remaining - 1, flags, chunk); +// } +// } + +// const a = new Decoder({ +// onReply(reply) { +// console.log('[REPLY]', reply); +// }, +// onErrorReply(err) { +// console.log('[ERROR REPLY]', err); +// }, +// onPush(push) { +// console.log('[PUSH]', push); +// }, +// getFlags() { +// return {}; +// } +// }); + +// a.write(Buffer.from('+PONG\r\n')); diff --git a/packages/client/lib/RESP/decoder.spec.ts b/packages/client/lib/RESP/decoder.spec.ts new file mode 100644 index 00000000000..cff4fbe047f --- /dev/null +++ b/packages/client/lib/RESP/decoder.spec.ts @@ -0,0 +1,195 @@ +// import { strict as assert } from 'assert'; +// import { SinonSpy, spy } from 'sinon'; +// import RESP2Decoder from './decoder'; +// import { ErrorReply } from '../../errors'; + +// interface DecoderAndSpies { +// decoder: RESP2Decoder; +// returnStringsAsBuffersSpy: SinonSpy; +// onReplySpy: SinonSpy; +// } + +// function createDecoderAndSpies(returnStringsAsBuffers: boolean): DecoderAndSpies { +// const returnStringsAsBuffersSpy = spy(() => returnStringsAsBuffers), +// onReplySpy = spy(); + +// return { +// decoder: new RESP2Decoder({ +// returnStringsAsBuffers: returnStringsAsBuffersSpy, +// onReply: onReplySpy +// }), +// returnStringsAsBuffersSpy, +// onReplySpy +// }; +// } + +// function writeChunks(stream: RESP2Decoder, buffer: Buffer) { +// let i = 0; +// while (i < buffer.length) { +// stream.write(buffer.slice(i, ++i)); +// } +// } + +// type Replies = Array>; + +// interface TestsOptions { +// toWrite: Buffer; +// returnStringsAsBuffers: boolean; +// replies: Replies; +// } + +// function generateTests({ +// toWrite, +// returnStringsAsBuffers, +// replies +// }: TestsOptions): void { +// it('single chunk', () => { +// const { decoder, returnStringsAsBuffersSpy, onReplySpy } = +// createDecoderAndSpies(returnStringsAsBuffers); +// decoder.write(toWrite); +// assert.equal(returnStringsAsBuffersSpy.callCount, replies.length); +// testReplies(onReplySpy, replies); +// }); + +// it('multiple chunks', () => { +// const { decoder, returnStringsAsBuffersSpy, onReplySpy } = +// createDecoderAndSpies(returnStringsAsBuffers); +// writeChunks(decoder, toWrite); +// assert.equal(returnStringsAsBuffersSpy.callCount, replies.length); +// testReplies(onReplySpy, replies); +// }); +// } + +// function testReplies(spy: SinonSpy, replies: Replies): void { +// if (!replies) { +// assert.equal(spy.callCount, 0); +// return; +// } + +// assert.equal(spy.callCount, replies.length); +// for (const [i, reply] of replies.entries()) { +// assert.deepEqual( +// spy.getCall(i).args, +// reply +// ); +// } +// } + +// describe('RESP2Parser', () => { +// describe('Simple String', () => { +// describe('as strings', () => { +// generateTests({ +// toWrite: Buffer.from('+OK\r\n'), +// returnStringsAsBuffers: false, +// replies: [['OK']] +// }); +// }); + +// describe('as buffers', () => { +// generateTests({ +// toWrite: Buffer.from('+OK\r\n'), +// returnStringsAsBuffers: true, +// replies: [[Buffer.from('OK')]] +// }); +// }); +// }); + +// describe('Error', () => { +// generateTests({ +// toWrite: Buffer.from('-ERR\r\n'), +// returnStringsAsBuffers: false, +// replies: [[new ErrorReply('ERR')]] +// }); +// }); + +// describe('Integer', () => { +// describe('-1', () => { +// generateTests({ +// toWrite: Buffer.from(':-1\r\n'), +// returnStringsAsBuffers: false, +// replies: [[-1]] +// }); +// }); + +// describe('0', () => { +// generateTests({ +// toWrite: Buffer.from(':0\r\n'), +// returnStringsAsBuffers: false, +// replies: [[0]] +// }); +// }); +// }); + +// describe('Bulk String', () => { +// describe('null', () => { +// generateTests({ +// toWrite: Buffer.from('$-1\r\n'), +// returnStringsAsBuffers: false, +// replies: [[null]] +// }); +// }); + +// describe('as strings', () => { +// generateTests({ +// toWrite: Buffer.from('$2\r\naa\r\n'), +// returnStringsAsBuffers: false, +// replies: [['aa']] +// }); +// }); + +// describe('as buffers', () => { +// generateTests({ +// toWrite: Buffer.from('$2\r\naa\r\n'), +// returnStringsAsBuffers: true, +// replies: [[Buffer.from('aa')]] +// }); +// }); +// }); + +// describe('Array', () => { +// describe('null', () => { +// generateTests({ +// toWrite: Buffer.from('*-1\r\n'), +// returnStringsAsBuffers: false, +// replies: [[null]] +// }); +// }); + +// const arrayBuffer = Buffer.from( +// '*5\r\n' + +// '+OK\r\n' + +// '-ERR\r\n' + +// ':0\r\n' + +// '$1\r\na\r\n' + +// '*0\r\n' +// ); + +// describe('as strings', () => { +// generateTests({ +// toWrite: arrayBuffer, +// returnStringsAsBuffers: false, +// replies: [[[ +// 'OK', +// new ErrorReply('ERR'), +// 0, +// 'a', +// [] +// ]]] +// }); +// }); + +// describe('as buffers', () => { +// generateTests({ +// toWrite: arrayBuffer, +// returnStringsAsBuffers: true, +// replies: [[[ +// Buffer.from('OK'), +// new ErrorReply('ERR'), +// 0, +// Buffer.from('a'), +// [] +// ]]] +// }); +// }); +// }); +// }); diff --git a/packages/client/lib/RESP/decoder.ts b/packages/client/lib/RESP/decoder.ts new file mode 100644 index 00000000000..afac0356a4c --- /dev/null +++ b/packages/client/lib/RESP/decoder.ts @@ -0,0 +1,1159 @@ +// @ts-nocheck +import { VerbatimString } from './verbatim-string'; +import { SimpleError, BlobError, ErrorReply } from '../errors'; +import { Flags } from './types'; + +// https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md +export const RESP_TYPES = { + NULL: 95, // _ + BOOLEAN: 35, // # + NUMBER: 58, // : + BIG_NUMBER: 40, // ( + DOUBLE: 44, // , + SIMPLE_STRING: 43, // + + BLOB_STRING: 36, // $ + VERBATIM_STRING: 61, // = + SIMPLE_ERROR: 45, // - + BLOB_ERROR: 33, // ! + ARRAY: 42, // * + SET: 126, // ~ + MAP: 37, // % + PUSH: 62 // > +} as const; + +const ASCII = { + '\r': 13, + 't': 116, + '+': 43, + '-': 45, + '0': 48, + '.': 46, + 'i': 105, + 'n': 110, + 'E': 69, + 'e': 101 +} as const; + +export const PUSH_FLAGS = { + [RESP_TYPES.BLOB_STRING]: Buffer +}; + +// this was written with performance in mind, so it's not very readable... sorry :( +// using `private _` instead of `#` to boost performance + +interface DecoderOptions { + onReply(reply: any): unknown; + onErrorReply(err: ErrorReply): unknown; + onPush(push: Array): unknown; + getFlags(): Flags; +} + +export class Decoder { + private _config; + + private _cursor = 0; + + private _next; + + constructor(config: DecoderOptions) { + this._config = config; + } + + reset() { + this._cursor = 0; + this._next = undefined; + } + + write(chunk) { + if (this._cursor >= chunk.length) { + this._cursor -= chunk.length; + return; + } + + if (this._next) { + if (this._next(chunk) || this._cursor >= chunk.length) { + this._cursor -= chunk.length; + return; + } + } + + do { + const type = chunk[this._cursor]; + if (++this._cursor === chunk.length) { + this._next = this._continueDecodeTypeValue.bind(this, type); + break; + } + + if (this._decodeTypeValue(type, chunk)) { + break; + } + } while (this._cursor < chunk.length); + this._cursor -= chunk.length; + } + + private _continueDecodeTypeValue(type, chunk) { + this._next = undefined; + return this._decodeTypeValue(type, chunk); + } + + private _decodeTypeValue(type, chunk) { + switch (type) { + case RESP_TYPES.NULL: + this._config.onReply(this._decodeNull()); + return false; + + case RESP_TYPES.BOOLEAN: + return this._handleDecodedValue( + this._config.onReply, + this._decodeBoolean(chunk) + ); + + case RESP_TYPES.NUMBER: + return this._handleDecodedValue( + this._config.onReply, + this._decodeNumber(chunk) + ); + + case RESP_TYPES.BIG_NUMBER: + return this._handleDecodedValue( + this._config.onReply, + this._decodeBigNumber( + this._config.getFlags()[RESP_TYPES.BIG_NUMBER], + chunk + ) + ); + + case RESP_TYPES.DOUBLE: + return this._handleDecodedValue( + this._config.onReply, + this._decodeDouble( + this._config.getFlags()[RESP_TYPES.DOUBLE], + chunk + ) + ); + + case RESP_TYPES.SIMPLE_STRING: + return this._handleDecodedValue( + this._config.onReply, + this._decodeSimpleString( + this._config.getFlags()[RESP_TYPES.SIMPLE_STRING], + chunk + ) + ); + + case RESP_TYPES.BLOB_STRING: + return this._handleDecodedValue( + this._config.onReply, + this._decodeBlobString( + this._config.getFlags()[RESP_TYPES.BLOB_STRING], + chunk + ) + ); + + case RESP_TYPES.VERBATIM_STRING: + return this._handleDecodedValue( + this._config.onReply, + this._decodeVerbatimString( + this._config.getFlags()[RESP_TYPES.VERBATIM_STRING], + chunk + ) + ); + + case RESP_TYPES.SIMPLE_ERROR: + return this._handleDecodedValue( + this._config.onErrorReply, + this._decodeSimpleError(chunk) + ); + + case RESP_TYPES.BLOB_ERROR: + return this._handleDecodedValue( + this._config.onErrorReply, + this._decodeBlobError(chunk) + ); + + case RESP_TYPES.ARRAY: + return this._handleDecodedValue( + this._config.onReply, + this._decodeArray(this._config.getFlags(), chunk) + ); + + case RESP_TYPES.SET: + return this._handleDecodedValue( + this._config.onReply, + this._decodeSet(this._config.getFlags(), chunk) + ); + + case RESP_TYPES.MAP: + return this._handleDecodedValue( + this._config.onReply, + this._decodeMap(this._config.getFlags(), chunk) + ); + + case RESP_TYPES.PUSH: + return this._handleDecodedValue( + this._config.onPush, + this._decodeArray(PUSH_FLAGS, chunk) + ); + } + } + + private _handleDecodedValue(cb, value) { + if (typeof value === 'function') { + this._next = this._continueDecodeValue.bind(this, cb, value); + return true; + } + + cb(value); + return false; + } + + private _continueDecodeValue(cb, next, chunk) { + this._next = undefined; + return this._handleDecodedValue(cb, next(chunk)); + } + + private _decodeNull() { + this._cursor += 2; // skip \r\n + return null; + } + + private _decodeBoolean(chunk) { + const boolean = chunk[this._cursor] === ASCII.t; + this._cursor += 3; // skip {t | f}\r\n + return boolean; + } + + private _decodeNumber(chunk) { + switch (chunk[this._cursor]) { + case ASCII['+']: + return this._maybeDecodeNumberValue(false, chunk); + + case ASCII['-']: + return this._maybeDecodeNumberValue(true, chunk); + + default: + return this._decodeNumberValue( + false, + this._decodeUnsingedNumber.bind(this, 0), + chunk + ); + } + } + + private _maybeDecodeNumberValue(isNegative, chunk) { + const cb = this._decodeUnsingedNumber.bind(this, 0); + return ++this._cursor === chunk.length ? + this._decodeNumberValue.bind(isNegative, cb) : + this._decodeNumberValue(isNegative, cb, chunk); + } + + private _decodeNumberValue(isNegative, numberCb, chunk) { + const number = numberCb(chunk); + return typeof number === 'function' ? + this._decodeNumberValue.bind(this, isNegative, number) : + isNegative ? -number : number; + } + + private _decodeUnsingedNumber(number, chunk) { + let cursor = this._cursor; + do { + const byte = chunk[cursor]; + if (byte === ASCII['\r']) { + this._cursor = cursor + 2; // skip \r\n + return number; + } + number = number * 10 + byte - ASCII['0']; + } while (++cursor < chunk.length); + + this._cursor = cursor; + return this._decodeUnsingedNumber.bind(this, number); + } + + private _decodeBigNumber(flag, chunk) { + if (flag === String) { + return this._decodeSimpleString(String, chunk); + } + + switch (chunk[this._cursor]) { + case ASCII['+']: + return this._maybeDecodeBigNumberValue(false, chunk); + + case ASCII['-']: + return this._maybeDecodeBigNumberValue(true, chunk); + + default: + return this._decodeBigNumberValue( + false, + this._decodeUnsingedBigNumber.bind(this, 0n), + chunk + ); + } + } + + private _maybeDecodeBigNumberValue(isNegative, chunk) { + const cb = this._decodeUnsingedBigNumber.bind(this, 0n); + return this._cursor === chunk.length ? + this._decodeBigNumberValue.bind(isNegative, cb) : + this._decodeBigNumberValue(isNegative, cb, chunk); + } + + private _decodeBigNumberValue(isNegative, bigNumberCb, chunk) { + const bigNumber = bigNumberCb(chunk); + return typeof bigNumber === 'function' ? + this._decodeBigNumberValue.bind(this, isNegative, bigNumber) : + isNegative ? -bigNumber : bigNumber; + } + + private _decodeUnsingedBigNumber(bigNumber, chunk) { + let cursor = this._cursor; + do { + const byte = chunk[cursor]; + if (byte === ASCII['\r']) { + this._cursor = cursor + 2; // skip \r\n + return bigNumber; + } + bigNumber = bigNumber * 10n + BigInt(byte - ASCII['0']); + } while (++cursor < chunk.length); + + this._cursor = cursor; + return this._decodeUnsingedBigNumber.bind(this, bigNumber); + } + + private _decodeDouble(flag, chunk) { + if (flag === String) { + return this._decodeSimpleString(String, chunk); + } + + switch (chunk[this._cursor]) { + case ASCII.n: + this._cursor += 5; // skip nan\r\n + return NaN; + + case ASCII['+']: + return this._maybeDecodeDoubleInteger(false, chunk); + + case ASCII['-']: + return this._maybeDecodeDoubleInteger(true, chunk); + + default: + return this._decodeDoubleInteger(false, 0, chunk); + } + } + + private _maybeDecodeDoubleInteger(isNegative, chunk) { + return ++this._cursor === chunk.length ? + this._decodeDoubleInteger.bind(this, isNegative, 0) : + this._decodeDoubleInteger(true, 0, chunk); + } + + private _decodeDoubleInteger(isNegative, integer, chunk) { + if (chunk[this._cursor] === ASCII.i) { + this._cursor += 5; // skip inf\r\n + return isNegative ? -Infinity : Infinity; + } + + return this._continueDecodeDoubleInteger(isNegative, integer, chunk); + } + + private _continueDecodeDoubleInteger(isNegative, integer, chunk) { + let cursor = this._cursor; + do { + const byte = chunk[cursor]; + switch (byte) { + case ASCII['.']: + this._cursor = cursor + 1; // skip . + return cursor < chunk.length ? + this._decodeDoubleDecimal(isNegative, 0, integer, chunk) : + this._decodeDoubleDecimal.bind(this, isNegative, 0, integer); + + case ASCII.E: + case ASCII.e: + this._cursor = cursor + 1; // skip e + return this._decodeDoubleExponent(isNegative ? -integer : integer, chunk); + + case ASCII['\r']: + this._cursor = cursor + 2; // skip \r\n + return isNegative ? -integer : integer; + + default: + integer = integer * 10 + byte - ASCII['0']; + } + } while (++cursor < chunk.length); + + this._cursor = cursor; + return this._continueDecodeDoubleInteger.bind(this, isNegative, integer); + } + + // Precalculated multipliers for decimal points to improve performance + // "A Number only keeps about 17 decimal places of precision" + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number + static _DOUBLE_DECIMAL_MULTIPLIERS = [ + 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, + 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, + 1e-13, 1e-14, 1e-15, 1e-16, 1e-17 + ]; + + private _decodeDoubleDecimal(isNegative, decimalIndex, double, chunk) { + let cursor = this._cursor; + do { + const byte = chunk[cursor]; + switch (byte) { + case ASCII.E: + case ASCII.e: + this._cursor = cursor + 1; // skip e + const d = isNegative ? -double : double; + return this._cursor === chunk.length ? + this._decodeDoubleExponent.bind(this, d, false, 0) : + this._decodeDoubleExponent(d, false, 0, chunk); + + case ASCII['\r']: + this._cursor = cursor + 2; // skip \r\n + return isNegative ? -double : double; + } + + if (decimalIndex < Decoder._DOUBLE_DECIMAL_MULTIPLIERS.length) { + double += (byte - ASCII['0']) * Decoder._DOUBLE_DECIMAL_MULTIPLIERS[decimalIndex++]; + } + } while (++cursor < chunk.length); + + this._cursor = cursor; + return this._decodeDoubleDecimal.bind(this, isNegative, decimalIndex, double); + } + + private _decodeDoubleExponent(double, chunk) { + switch (chunk[this._cursor]) { + case ASCII['+']: + return ++this._cursor === chunk.length ? + this._continueDecodeDoubleExponent.bind(this, false, double, 0) : + this._continueDecodeDoubleExponent(false, double, 0, chunk); + + case ASCII['-']: + return ++this._cursor === chunk.length ? + this._continueDecodeDoubleExponent.bind(this, true, double, 0) : + this._continueDecodeDoubleExponent(true, double, 0, chunk); + } + + return this._continueDecodeDoubleExponent(false, double, 0, chunk); + } + + private _continueDecodeDoubleExponent(isNegative, double, exponent, chunk) { + let cursor = this._cursor; + do { + const byte = chunk[cursor]; + if (byte === ASCII['\r']) { + this._cursor = cursor + 2; // skip \r\n + return double * 10 ** (isNegative ? -exponent : exponent); + } + + exponent = exponent * 10 + byte - ASCII['0']; + } while (++cursor < chunk.length); + + this._cursor = cursor; + return this._continueDecodeDoubleExponent.bind(this, isNegative, double, exponent); + } + + private _findCRLF(chunk, cursor) { + while (chunk[cursor] !== ASCII['\r']) { + if (++cursor === chunk.length) { + this._cursor = chunk.length; + return -1; + } + } + + this._cursor = cursor + 2; // skip \r\n + return cursor; + } + + private _decodeSimpleString(flag, chunk) { + const start = this._cursor, + crlfIndex = this._findCRLF(chunk, start); + if (crlfIndex === -1) { + return this._continueDecodeSimpleString.bind( + this, + [chunk.subarray(start)], + flag + ); + } + + const slice = chunk.subarray(start, crlfIndex); + return flag === Buffer ? + slice : + slice.toString(); + } + + private _continueDecodeSimpleString(chunks, flag, chunk) { + const start = this._cursor, + crlfIndex = this._findCRLF(chunk, start); + if (crlfIndex === -1) { + chunks.push(chunk.subarray(start)); + return this._continueDecodeSimpleString.bind(this, chunks, flag); + } + + chunks.push(chunk.subarray(start, crlfIndex)); + return flag === Buffer ? + Buffer.concat(chunks) : + chunks.join(''); + } + + private _decodeBlobString(flag, chunk) { + // RESP 2 bulk string null + // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-bulk-strings + if (chunk[this._cursor] === ASCII['-']) { + this._cursor += 4; // skip -1\r\n + return null; + } + + const length = this._decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this._continueDecodeBlobStringLength.bind(this, length, flag); + } else if (this._cursor >= chunk.length) { + return this._decodeBlobStringWithLength.bind(this, length, flag); + } + + return this._decodeBlobStringWithLength(length, flag, chunk); + } + + private _continueDecodeBlobStringLength(lengthCb, flag, chunk) { + const length = lengthCb(chunk); + if (typeof length === 'function') { + return this._continueDecodeBlobStringLength.bind(this, length, flag); + } else if (this._cursor >= chunk.length) { + return this._decodeBlobStringWithLength.bind(this, length, flag); + } + + return this._decodeBlobStringWithLength(length, flag, chunk); + } + + private _decodeStringWithLength(length, skip, flag, chunk) { + const end = this._cursor + length; + if (end >= chunk.length) { + const slice = chunk.subarray(this._cursor); + this._cursor = chunk.length; + return this._continueDecodeStringWithLength.bind( + this, + length - slice.length, + [slice], + skip, + flag + ); + } + + const slice = chunk.subarray(this._cursor, end); + this._cursor = end + skip; + return flag === Buffer ? + slice : + slice.toString(); + } + + private _continueDecodeStringWithLength(length, chunks, skip, flag, chunk) { + const end = this._cursor + length; + if (end >= chunk.length) { + const slice = chunk.subarray(this._cursor); + chunks.push(slice); + this._cursor = chunk.length; + return this._continueDecodeStringWithLength.bind( + this, + length - slice.length, + chunks, + flag + ); + } + + chunks.push(chunk.subarray(this._cursor, end)); + this._cursor = end + skip; + return flag === Buffer ? + Buffer.concat(chunks) : + chunks.join(''); + } + + private _decodeBlobStringWithLength(length, flag, chunk) { + return this._decodeStringWithLength(length, 2, flag, chunk); + } + + private _decodeVerbatimString(flag, chunk) { + return this._continueDecodeVerbatimStringLength( + this._decodeUnsingedNumber.bind(this, 0), + flag, + chunk + ); + } + + private _continueDecodeVerbatimStringLength(lengthCb, flag, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function'? + this._continueDecodeVerbatimStringLength.bind(this, length, flag) : + this._decodeVerbatimStringWithLength(length, flag, chunk); + } + + private _decodeVerbatimStringWithLength(length, flag, chunk) { + const stringLength = length - 4; // skip : + if (flag === VerbatimString) { + return this._decodeVerbatimStringFormat(stringLength, chunk); + } + + this._cursor += 4; // skip : + return this._cursor >= chunk.length ? + this._decodeBlobStringWithLength.bind(this, stringLength, flag) : + this._decodeBlobStringWithLength(stringLength, flag, chunk); + } + + private _decodeVerbatimStringFormat(stringLength, chunk) { + return this._continueDecodeVerbatimStringFormat( + stringLength, + this._decodeStringWithLength.bind(this, 3, 1, String), + chunk + ); + } + + private _continueDecodeVerbatimStringFormat(stringLength, formatCb, chunk) { + const format = formatCb(chunk); + return typeof format === 'function' ? + this._continueDecodeVerbatimStringFormat.bind(this, stringLength, format) : + this._decodeVerbatimStringWithFormat(stringLength, format, chunk); + } + + private _decodeVerbatimStringWithFormat(stringLength, format, chunk) { + return this._continueDecodeVerbatimStringWithFormat( + format, + this._decodeBlobStringWithLength.bind(this, stringLength, String), + chunk + ); + } + + private _continueDecodeVerbatimStringWithFormat(format, stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this._continueDecodeVerbatimStringWithFormat.bind(this, format, string) : + new VerbatimString(format, string); + } + + private _decodeSimpleError(chunk) { + const string = this._decodeSimpleString(String, chunk); + return typeof string === 'function' ? + this._continueDecodeSimpleError.bind(this, string) : + new SimpleError(string); + } + + private _continueDecodeSimpleError(stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this._continueDecodeSimpleError.bind(this, string) : + new SimpleError(string); + } + + private _decodeBlobError(chunk) { + const string = this._decodeBlobString(String, chunk); + return typeof string === 'function' ? + this._continueDecodeBlobError.bind(this, string) : + new BlobError(string); + } + + private _continueDecodeBlobError(stringCb, chunk) { + const string = stringCb(chunk); + return typeof string === 'function' ? + this._continueDecodeBlobError.bind(this, string) : + new BlobError(string); + } + + private _decodeNestedType(flags, chunk) { + const type = chunk[this._cursor]; + return ++this._cursor === chunk.length ? + this._decodeNestedTypeValue.bind(this, type, flags) : + this._decodeNestedTypeValue(type, flags, chunk); + } + + private _decodeNestedTypeValue(type, flags, chunk) { + switch (type) { + case RESP_TYPES.NULL: + return this._decodeNull(); + + case RESP_TYPES.BOOLEAN: + return this._decodeBoolean(chunk); + + case RESP_TYPES.NUMBER: + return this._decodeNumber(chunk); + + case RESP_TYPES.BIG_NUMBER: + return this._decodeBigNumber(flags[RESP_TYPES.BIG_NUMBER], chunk); + + case RESP_TYPES.DOUBLE: + return this._decodeDouble(flags[RESP_TYPES.DOUBLE], chunk); + + case RESP_TYPES.SIMPLE_STRING: + return this._decodeSimpleString(flags[RESP_TYPES.SIMPLE_STRING], chunk); + + case RESP_TYPES.BLOB_STRING: + return this._decodeBlobString(flags[RESP_TYPES.BLOB_STRING], chunk); + + case RESP_TYPES.VERBATIM_STRING: + return this._decodeVerbatimString(flags[RESP_TYPES.VERBATIM_STRING], chunk); + + case RESP_TYPES.SIMPLE_ERROR: + return this._decodeSimpleError(chunk); + + case RESP_TYPES.BLOB_ERROR: + return this._decodeBlobError(chunk); + + case RESP_TYPES.ARRAY: + return this._decodeArray(flags, chunk); + + case RESP_TYPES.SET: + return this._decodeSet(flags, chunk); + + case RESP_TYPES.MAP: + return this._decodeMap(flags, chunk); + } + } + + private _decodeArray(flags, chunk) { + // RESP 2 null + // https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-arrays + if (chunk[this._cursor] === ASCII['-']) { + this._cursor += 4; // skip -1\r\n + return null; + } + + return this._decodeArrayWithLength( + this._decodeUnsingedNumber(0, chunk), + flags, + chunk + ); + } + + private _decodeArrayWithLength(length, flags, chunk) { + return typeof length === 'function' ? + this._continueDecodeArrayLength.bind(this, length, flags) : + this._decodeArrayItems( + new Array(length), + 0, + flags, + chunk + ); + } + + private _continueDecodeArrayLength(lengthCb, flags, chunk) { + return this._decodeArrayWithLength( + lengthCb(chunk), + flags, + chunk + ); + } + + private _decodeArrayItems(array, filled, flags, chunk) { + for (let i = filled; i < array.length; i++) { + if (this._cursor >= chunk.length) { + return this._decodeArrayItems.bind( + this, + array, + i, + flags + ); + } + + const item = this._decodeNestedType(flags, chunk); + if (typeof item === 'function') { + return this._continueDecodeArrayItems.bind( + this, + array, + i, + item, + flags + ); + } + + array[i] = item; + } + + return array; + } + + private _continueDecodeArrayItems(array, filled, itemCb, flags, chunk) { + const item = itemCb(chunk); + if (typeof item === 'function') { + return this._continueDecodeArrayItems.bind( + this, + array, + filled, + item, + flags + ); + } + + array[filled++] = item; + + return this._decodeArrayItems(array, filled, flags, chunk); + } + + private _decodeSet(flags, chunk) { + const length = this._decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this._continueDecodeSetLength.bind(this, length, flags); + } + + return this._decodeSetItems( + length, + flags, + chunk + ); + } + + private _continueDecodeSetLength(lengthCb, flags, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function' ? + this._continueDecodeSetLength.bind(this, length, flags) : + this._decodeSetItems(length, flags, chunk); + } + + private _decodeSetItems(length, flags, chunk) { + return flags[RESP_TYPES.SET] === Set ? + this._decodeSetAsSet( + new Set(), + length, + flags, + chunk + ) : + this._decodeArrayItems( + new Array(length), + 0, + flags, + chunk + ); + } + + private _decodeSetAsSet(set, remaining, flags, chunk) { + // using `remaining` instead of `length` & `set.size` to make it work even if the set contains duplicates + while (remaining > 0) { + if (this._cursor >= chunk.length) { + return this._decodeSetAsSet.bind( + this, + set, + remaining, + flags + ); + } + + const item = this._decodeNestedType(flags, chunk); + if (typeof item === 'function') { + return this._continueDecodeSetAsSet.bind( + this, + set, + remaining, + item, + flags + ); + } + + set.add(item); + --remaining; + } + + return set; + } + + private _continueDecodeSetAsSet(set, remaining, itemCb, flags, chunk) { + const item = itemCb(chunk); + if (typeof item === 'function') { + return this._continueDecodeSetAsSet.bind( + this, + set, + remaining, + item, + flags + ); + } + + set.add(item); + + return this._decodeSetAsSet(set, remaining - 1, flags, chunk); + } + + private _decodeMap(flags, chunk) { + const length = this._decodeUnsingedNumber(0, chunk); + if (typeof length === 'function') { + return this._continueDecodeMapLength.bind(this, length, flags); + } + + return this._decodeMapItems( + length, + flags, + chunk + ); + } + + private _continueDecodeMapLength(lengthCb, flags, chunk) { + const length = lengthCb(chunk); + return typeof length === 'function' ? + this._continueDecodeMapLength.bind(this, length, flags) : + this._decodeMapItems(length, flags, chunk); + } + + private _decodeMapItems(length, flags, chunk) { + switch (flags[RESP_TYPES.MAP]) { + case Map: + return this._decodeMapAsMap( + new Map(), + length, + flags, + chunk + ); + + case Array: + return this._decodeArrayItems( + new Array(length * 2), + 0, + flags, + chunk + ); + + default: + return this._decodeMapAsObject( + Object.create(null), + length, + flags, + chunk + ); + } + } + + private _decodeMapAsMap(map, remaining, flags, chunk) { + // using `remaining` instead of `length` & `map.size` to make it work even if the map contains duplicate keys + while (remaining > 0) { + if (this._cursor >= chunk.length) { + return this._decodeMapAsMap.bind( + this, + map, + remaining, + flags + ); + } + + const key = this._decodeMapKey(flags, chunk); + if (typeof key === 'function') { + return this._continueDecodeMapKey.bind( + this, + map, + remaining, + key, + flags + ); + } + + if (this._cursor >= chunk.length) { + return this._continueDecodeMapValue.bind( + this, + map, + remaining, + key, + this._decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this._decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this._continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + flags + ); + } + + map.set(key, value); + --remaining; + } + + return map; + } + + private _decodeMapKey(flags, chunk) { + const type = chunk[this._cursor]; + return ++this._cursor === chunk.length ? + this._decodeMapKeyValue.bind(this, type, flags) : + this._decodeMapKeyValue(type, flags, chunk); + } + + private _decodeMapKeyValue(type, flags, chunk) { + switch (type) { + // decode simple string map key as string (and not as buffer) + case RESP_TYPES.SIMPLE_STRING: + return this._decodeSimpleString(String, chunk); + + // decode blob string map key as string (and not as buffer) + case RESP_TYPES.BLOB_STRING: + return this._decodeBlobString(String, chunk); + + default: + return this._decodeNestedTypeValue(type, flags, chunk); + } + } + + private _continueDecodeMapKey(map, remaining, keyCb, flags, chunk) { + const key = keyCb(chunk); + if (typeof key === 'function') { + return this._continueDecodeMapKey.bind( + this, + map, + remaining, + key, + flags + ); + } + + if (this._cursor >= chunk.length) { + return this._continueDecodeMapValue.bind( + this, + map, + remaining, + key, + this._decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this._decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this._continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + flags + ); + } + + map.set(key, value); + return this._decodeMapAsMap(map, remaining - 1, flags, chunk); + } + + private _continueDecodeMapValue(map, remaining, key, valueCb, flags, chunk) { + const value = valueCb(chunk); + if (typeof value === 'function') { + return this._continueDecodeMapValue.bind( + this, + map, + remaining, + key, + value, + flags + ); + } + + map.set(key, value); + + return this._decodeMapAsMap(map, remaining - 1, flags, chunk); + } + + private _decodeMapAsObject(object, remaining, flags, chunk) { + while (remaining > 0) { + if (this._cursor >= chunk.length) { + return this._decodeMapAsObject.bind( + this, + object, + remaining, + flags + ); + } + + const key = this._decodeMapKey(flags, chunk); + if (typeof key === 'function') { + return this._continueDecodeMapAsObjectKey.bind( + this, + object, + remaining, + key, + flags + ); + } + + if (this._cursor >= chunk.length) { + return this._continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + this._decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this._decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this._continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + flags + ); + } + + object[key] = value; + --remaining; + } + + return object; + } + + private _continueDecodeMapAsObjectKey(object, remaining, keyCb, flags, chunk) { + const key = keyCb(chunk); + if (typeof key === 'function') { + return this._continueDecodeMapAsObjectKey.bind( + this, + object, + remaining, + key, + flags + ); + } + + if (this._cursor >= chunk.length) { + return this._continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + this._decodeNestedType.bind(this, flags), + flags + ); + } + + const value = this._decodeNestedType(flags, chunk); + if (typeof value === 'function') { + return this._continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + flags + ); + } + + object[key] = value; + + return this._decodeMapAsObject(object, remaining - 1, flags, chunk); + } + + private _continueDecodeMapAsObjectValue(object, remaining, key, valueCb, flags, chunk) { + const value = valueCb(chunk); + if (typeof value === 'function') { + return this._continueDecodeMapAsObjectValue.bind( + this, + object, + remaining, + key, + value, + flags + ); + } + + object[key] = value; + + return this._decodeMapAsObject(object, remaining - 1, flags, chunk); + } +} diff --git a/packages/client/lib/RESP/encoder.spec.ts b/packages/client/lib/RESP/encoder.spec.ts new file mode 100644 index 00000000000..f8f20d783b3 --- /dev/null +++ b/packages/client/lib/RESP/encoder.spec.ts @@ -0,0 +1,33 @@ +import { strict as assert } from 'assert'; +import { describe } from 'mocha'; +import encodeCommand from './encoder'; + +describe('RESP Encoder', () => { + it('1 byte', () => { + assert.deepEqual( + encodeCommand(['a', 'z']), + ['*2\r\n$1\r\na\r\n$1\r\nz\r\n'] + ); + }); + + it('2 bytes', () => { + assert.deepEqual( + encodeCommand(['א', 'ת']), + ['*2\r\n$2\r\nא\r\n$2\r\nת\r\n'] + ); + }); + + it('4 bytes', () => { + assert.deepEqual( + [...encodeCommand(['🐣', '🐤'])], + ['*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n'] + ); + }); + + it('buffer', () => { + assert.deepEqual( + encodeCommand([Buffer.from('string')]), + ['*1\r\n$6\r\n', Buffer.from('string'), '\r\n'] + ); + }); +}); diff --git a/packages/client/lib/RESP/encoder.ts b/packages/client/lib/RESP/encoder.ts new file mode 100644 index 00000000000..b1db1c70237 --- /dev/null +++ b/packages/client/lib/RESP/encoder.ts @@ -0,0 +1,28 @@ +import { RedisArgument } from "./types"; + +const CRLF = '\r\n'; + +export default function encodeCommand(args: Array): Array { + const toWrite: Array = []; + + let strings = '*' + args.length + CRLF; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (typeof arg === 'string') { + strings += '$' + Buffer.byteLength(arg) + CRLF + arg + CRLF; + } else if (arg instanceof Buffer) { + toWrite.push( + strings + '$' + arg.length.toString() + CRLF, + arg + ); + strings = CRLF; + } else { + throw new TypeError(`"arguments[${i}]" must be of type "string | Buffer", got ${typeof arg} instead.`); + } + } + + toWrite.push(strings); + + return toWrite; +} diff --git a/packages/client/lib/RESP/types.ts b/packages/client/lib/RESP/types.ts new file mode 100644 index 00000000000..d6b11db72b6 --- /dev/null +++ b/packages/client/lib/RESP/types.ts @@ -0,0 +1,417 @@ +import { RedisScriptConfig, SHA1 } from '../lua-script'; +import { RESP_TYPES } from './decoder'; +import { VerbatimString } from './verbatim-string'; + +export type RESP_TYPES = typeof RESP_TYPES; + +export type RespTypes = RESP_TYPES[keyof RESP_TYPES]; + +type RespType< + RESP_TYPE extends RespTypes, + DEFAULT, + TYPES = never, + FLAG_TYPES = DEFAULT | TYPES +> = (DEFAULT | TYPES) & { + RESP_TYPE: RESP_TYPE; + DEFAULT: DEFAULT; + TYPES: TYPES; + FLAG: Flag; +}; + +export type NullReply = RespType< + RESP_TYPES['NULL'], + null +>; +export type BooleanReply< + T extends boolean = boolean +> = RespType< + RESP_TYPES['BOOLEAN'], + T +>; +export type NumberReply< + T extends number = number +> = RespType< + RESP_TYPES['NUMBER'], + T, + `${T}`, + number | string +>; +export type BigNumberReply< + T extends bigint = bigint +> = RespType< + RESP_TYPES['BIG_NUMBER'], + T, + number | `${T}`, + bigint | number | string +>; +export type DoubleReply< + T extends number = number +> = RespType< + RESP_TYPES['DOUBLE'], + T, + `${T}`, + number | string +>; +export type SimpleStringReply< + T extends string = string +> = RespType< + RESP_TYPES['SIMPLE_STRING'], + T, + Buffer, + string | Buffer +>; +export type BlobStringReply< + T extends string = string +> = RespType< + RESP_TYPES['BLOB_STRING'], + T, + Buffer, + string | Buffer +>; +export type VerbatimStringReply< + T extends string = string +> = RespType< + RESP_TYPES['VERBATIM_STRING'], + T, + Buffer | VerbatimString, + string | Buffer | VerbatimString +>; +export type SimpleErrorReply = RespType< + RESP_TYPES['SIMPLE_ERROR'], + Buffer +>; +export type BlobErrorReply = RespType< + RESP_TYPES['BLOB_ERROR'], + Buffer +>; +export type ArrayReply = RespType< + RESP_TYPES['ARRAY'], + Array, + never, + Array +>; +export type TuplesReply]> = RespType< + RESP_TYPES['ARRAY'], + T, + never, + Array +>; +export type SetReply = RespType< + RESP_TYPES['SET'], + Array, + Set, + Array | Set +>; +export type MapReply = RespType< + RESP_TYPES['MAP'], + { [key: string]: V }, + Map | Array, + Map | Array +>; + +type MapKeyValue = [key: BlobStringReply, value: unknown]; + +type MapTuples = Array; + +export type TuplesToMapReply = RespType< + RESP_TYPES['MAP'], + { + [P in T[number] as P[0] extends BlobStringReply ? S : never]: P[1]; + }, + Map | FlattenTuples +>; + +type FlattenTuples = ( + T extends [] ? [] : + T extends [MapKeyValue] ? T[0] : + T extends [MapKeyValue, ...infer R] ? [ + ...T[0], + ...FlattenTuples + ] : + never +); + +export type ReplyUnion = NullReply | BooleanReply | NumberReply | BigNumberReply | DoubleReply | SimpleStringReply | BlobStringReply | VerbatimStringReply | SimpleErrorReply | BlobErrorReply | + // cannot reuse ArrayReply, SetReply and MapReply because of circular reference + RespType< + RESP_TYPES['ARRAY'], + Array + > | + RespType< + RESP_TYPES['SET'], + Array, + Set + > | + RespType< + RESP_TYPES['MAP'], + { [key: string]: ReplyUnion }, + Map | Array + >; + +export type Reply = ReplyWithFlags; + +export type Flag = ((...args: any) => T) | (new (...args: any) => T); + +type RespTypeUnion = T extends RespType ? FLAG_TYPES : never; + +export type Flags = { + [P in RespTypes]?: Flag>>>; +}; + +type MapKey< + T, + FLAGS extends Flags +> = ReplyWithFlags; + +export type ReplyWithFlags< + REPLY, + FLAGS extends Flags +> = ( + // if REPLY is a type, extract the coresponding type from FLAGS or use the default type + REPLY extends RespType ? + FLAGS[RESP_TYPE] extends Flag ? + ReplyWithFlags, FLAGS> : + ReplyWithFlags + : ( + // if REPLY is a known generic type, convert its generic arguments + // TODO: tuples? + REPLY extends Array ? Array> : + REPLY extends Set ? Set> : + REPLY extends Map ? Map, ReplyWithFlags> : + // `Date` & `Buffer` are supersets of `Record`, so they need to be checked first + REPLY extends Date ? REPLY : + REPLY extends Buffer ? REPLY : + REPLY extends Record ? { + [P in keyof REPLY]: ReplyWithFlags; + } : + // otherwise, just return the REPLY as is + REPLY + ) +); + +export type TransformReply = (this: void, reply: any, preserve?: any) => any; // TODO; + +export type RedisArgument = string | Buffer; + +export type CommandArguments = Array & { preserve?: unknown }; + +export const REQUEST_POLICIES = { + /** + * TODO + */ + ALL_NODES: 'all_nodes', + /** + * TODO + */ + ALL_SHARDS: 'all_shards', + /** + * TODO + */ + SPECIAL: 'special' +} as const; + +export type REQUEST_POLICIES = typeof REQUEST_POLICIES; + +export type RequestPolicies = REQUEST_POLICIES[keyof REQUEST_POLICIES]; + +export const RESPONSE_POLICIES = { + /** + * TODO + */ + ONE_SUCCEEDED: 'one_succeeded', + /** + * TODO + */ + ALL_SUCCEEDED: 'all_succeeded', + /** + * TODO + */ + LOGICAL_AND: 'agg_logical_and', + /** + * TODO + */ + SPECIAL: 'special' +} as const; + +export type RESPONSE_POLICIES = typeof RESPONSE_POLICIES; + +export type ResponsePolicies = RESPONSE_POLICIES[keyof RESPONSE_POLICIES]; + +export type CommandPolicies = { + request?: RequestPolicies | null; + response?: ResponsePolicies | null; +}; + +export type Command = { + FIRST_KEY_INDEX?: number | ((this: void, ...args: Array) => RedisArgument | undefined); + IS_READ_ONLY?: boolean; + POLICIES?: CommandPolicies; + transformArguments(this: void, ...args: Array): CommandArguments; + TRANSFORM_LEGACY_REPLY?: boolean; + transformReply: TransformReply | Record; +}; + +export type RedisCommands = Record; + +export type RedisModules = Record; + +export interface RedisFunction extends Command { + NUMBER_OF_KEYS?: number; +} + +export type RedisFunctions = Record>; + +export type RedisScript = RedisScriptConfig & SHA1; + +export type RedisScripts = Record; + +// TODO: move to Commander? +export interface CommanderConfig< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> { + modules?: M; + functions?: F; + scripts?: S; + /** + * TODO + */ + RESP?: RESP; +} + +type Resp2Array = ( + T extends [] ? [] : + T extends [infer ITEM] ? [Resp2Reply] : + T extends [infer ITEM, ...infer REST] ? [ + Resp2Reply, + ...Resp2Array + ] : + T extends Array ? Array> : + never +); + +export type Resp2Reply = ( + RESP3REPLY extends RespType ? + // TODO: RESP3 only scalar types + RESP_TYPE extends RESP_TYPES['DOUBLE'] ? BlobStringReply : + RESP_TYPE extends RESP_TYPES['ARRAY'] | RESP_TYPES['SET'] ? RespType< + RESP_TYPE, + Resp2Array + > : + RESP_TYPE extends RESP_TYPES['MAP'] ? RespType< + RESP_TYPES['ARRAY'], + Resp2Array>> + > : + RespType< + RESP_TYPE, + DEFAULT, + TYPES + > : + RESP3REPLY +); + +export type RespVersions = 2 | 3; + +export type CommandReply< + COMMAND extends Command, + RESP extends RespVersions +> = ( + // if transformReply is a function, use its return type + COMMAND['transformReply'] extends (...args: any) => infer T ? T : + // if transformReply[RESP] is a function, use its return type + COMMAND['transformReply'] extends Record infer T> ? T : + // otherwise use the generic reply type + Reply +); + +export type CommandSignature< + COMMAND extends Command, + RESP extends RespVersions, + FLAGS extends Flags +> = (...args: Parameters) => Promise, FLAGS>>; + +export type CommandWithPoliciesSignature< + COMMAND extends Command, + RESP extends RespVersions, + FLAGS extends Flags, + POLICIES extends CommandPolicies +> = (...args: Parameters) => Promise< + ReplyWithPolicy< + ReplyWithFlags, FLAGS>, + MergePolicies + > +>; + +export type MergePolicies< + COMMAND extends Command, + POLICIES extends CommandPolicies +> = Omit & POLICIES; + +type ReplyWithPolicy< + REPLY, + POLICIES extends CommandPolicies, +> = ( + POLICIES['request'] extends REQUEST_POLICIES['SPECIAL'] ? never : + POLICIES['request'] extends null | undefined ? REPLY : + unknown extends POLICIES['request'] ? REPLY : + POLICIES['response'] extends RESPONSE_POLICIES['SPECIAL'] ? never : + POLICIES['response'] extends RESPONSE_POLICIES['ALL_SUCCEEDED' | 'ONE_SUCCEEDED' | 'LOGICAL_AND'] ? REPLY : + // otherwise, return array of replies + Array +); + +const SAME = { + transformArguments(key: string): Array { + return ['GET', key]; + }, + transformReply: () => 'default' as const +} satisfies Command; + +type SAME_DEFAULT = CommandWithPoliciesSignature< + typeof SAME, + 2, + {}, + { + request: REQUEST_POLICIES['ALL_NODES']; + response: RESPONSE_POLICIES['SPECIAL']; + } +>; + +// type SAME_RESP2 = CommandReply; +// type SAME_COMMAND_RESP2 = CommandSignuture; +// type SAME_RESP3 = CommandReply; +// type SAME_COMMAND_RESP3 = CommandSignuture; + +// interface Test { +// /** +// * This is a test +// */ +// a: 'a'; +// } + +// const DIFFERENT = { +// transformArguments(key: string): Array { +// return ['GET', key]; +// }, +// transformReply: { +// 2: () => null as any as Test, +// 3: () => '3' as const +// } +// } satisfies Command; + +// type DIFFERENT_RESP2 = CommandReply; +// type DIFFERENT_COMMAND_RESP2 = CommandSignuture; +// type DIFFERENT_RESP3 = CommandReply; +// type DIFFERENT_COMMAND_RESP3 = CommandSignuture; + +// const a = null as any as DIFFERENT_COMMAND_RESP2; + +// const b = await a('a'); + +// b.a \ No newline at end of file diff --git a/packages/client/lib/RESP/verbatim-string.ts b/packages/client/lib/RESP/verbatim-string.ts new file mode 100644 index 00000000000..92ff4fe3fb1 --- /dev/null +++ b/packages/client/lib/RESP/verbatim-string.ts @@ -0,0 +1,8 @@ +export class VerbatimString extends String { + constructor( + public format: string, + value: string + ) { + super(value); + } +} diff --git a/packages/client/lib/client/RESP2/composers/buffer.spec.ts b/packages/client/lib/client/RESP2/composers/buffer.spec.ts deleted file mode 100644 index f57c369fecb..00000000000 --- a/packages/client/lib/client/RESP2/composers/buffer.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { strict as assert } from 'assert'; -import BufferComposer from './buffer'; - -describe('Buffer Composer', () => { - const composer = new BufferComposer(); - - it('should compose two buffers', () => { - composer.write(Buffer.from([0])); - assert.deepEqual( - composer.end(Buffer.from([1])), - Buffer.from([0, 1]) - ); - }); -}); diff --git a/packages/client/lib/client/RESP2/composers/buffer.ts b/packages/client/lib/client/RESP2/composers/buffer.ts deleted file mode 100644 index 4affb4283e0..00000000000 --- a/packages/client/lib/client/RESP2/composers/buffer.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Composer } from './interface'; - -export default class BufferComposer implements Composer { - private chunks: Array = []; - - write(buffer: Buffer): void { - this.chunks.push(buffer); - } - - end(buffer: Buffer): Buffer { - this.write(buffer); - return Buffer.concat(this.chunks.splice(0)); - } - - reset() { - this.chunks = []; - } -} diff --git a/packages/client/lib/client/RESP2/composers/interface.ts b/packages/client/lib/client/RESP2/composers/interface.ts deleted file mode 100644 index 0fc8f031414..00000000000 --- a/packages/client/lib/client/RESP2/composers/interface.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface Composer { - write(buffer: Buffer): void; - - end(buffer: Buffer): T; - - reset(): void; -} diff --git a/packages/client/lib/client/RESP2/composers/string.spec.ts b/packages/client/lib/client/RESP2/composers/string.spec.ts deleted file mode 100644 index 9dd26aae021..00000000000 --- a/packages/client/lib/client/RESP2/composers/string.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { strict as assert } from 'assert'; -import StringComposer from './string'; - -describe('String Composer', () => { - const composer = new StringComposer(); - - it('should compose two strings', () => { - composer.write(Buffer.from([0])); - assert.deepEqual( - composer.end(Buffer.from([1])), - Buffer.from([0, 1]).toString() - ); - }); -}); diff --git a/packages/client/lib/client/RESP2/composers/string.ts b/packages/client/lib/client/RESP2/composers/string.ts deleted file mode 100644 index 0cd8f00e95c..00000000000 --- a/packages/client/lib/client/RESP2/composers/string.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { StringDecoder } from 'string_decoder'; -import { Composer } from './interface'; - -export default class StringComposer implements Composer { - private decoder = new StringDecoder(); - - private string = ''; - - write(buffer: Buffer): void { - this.string += this.decoder.write(buffer); - } - - end(buffer: Buffer): string { - const string = this.string + this.decoder.end(buffer); - this.string = ''; - return string; - } - - reset() { - this.string = ''; - } -} diff --git a/packages/client/lib/client/RESP2/decoder.spec.ts b/packages/client/lib/client/RESP2/decoder.spec.ts deleted file mode 100644 index dcce9f60115..00000000000 --- a/packages/client/lib/client/RESP2/decoder.spec.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { strict as assert } from 'assert'; -import { SinonSpy, spy } from 'sinon'; -import RESP2Decoder from './decoder'; -import { ErrorReply } from '../../errors'; - -interface DecoderAndSpies { - decoder: RESP2Decoder; - returnStringsAsBuffersSpy: SinonSpy; - onReplySpy: SinonSpy; -} - -function createDecoderAndSpies(returnStringsAsBuffers: boolean): DecoderAndSpies { - const returnStringsAsBuffersSpy = spy(() => returnStringsAsBuffers), - onReplySpy = spy(); - - return { - decoder: new RESP2Decoder({ - returnStringsAsBuffers: returnStringsAsBuffersSpy, - onReply: onReplySpy - }), - returnStringsAsBuffersSpy, - onReplySpy - }; -} - -function writeChunks(stream: RESP2Decoder, buffer: Buffer) { - let i = 0; - while (i < buffer.length) { - stream.write(buffer.slice(i, ++i)); - } -} - -type Replies = Array>; - -interface TestsOptions { - toWrite: Buffer; - returnStringsAsBuffers: boolean; - replies: Replies; -} - -function generateTests({ - toWrite, - returnStringsAsBuffers, - replies -}: TestsOptions): void { - it('single chunk', () => { - const { decoder, returnStringsAsBuffersSpy, onReplySpy } = - createDecoderAndSpies(returnStringsAsBuffers); - decoder.write(toWrite); - assert.equal(returnStringsAsBuffersSpy.callCount, replies.length); - testReplies(onReplySpy, replies); - }); - - it('multiple chunks', () => { - const { decoder, returnStringsAsBuffersSpy, onReplySpy } = - createDecoderAndSpies(returnStringsAsBuffers); - writeChunks(decoder, toWrite); - assert.equal(returnStringsAsBuffersSpy.callCount, replies.length); - testReplies(onReplySpy, replies); - }); -} - -function testReplies(spy: SinonSpy, replies: Replies): void { - if (!replies) { - assert.equal(spy.callCount, 0); - return; - } - - assert.equal(spy.callCount, replies.length); - for (const [i, reply] of replies.entries()) { - assert.deepEqual( - spy.getCall(i).args, - reply - ); - } -} - -describe('RESP2Parser', () => { - describe('Simple String', () => { - describe('as strings', () => { - generateTests({ - toWrite: Buffer.from('+OK\r\n'), - returnStringsAsBuffers: false, - replies: [['OK']] - }); - }); - - describe('as buffers', () => { - generateTests({ - toWrite: Buffer.from('+OK\r\n'), - returnStringsAsBuffers: true, - replies: [[Buffer.from('OK')]] - }); - }); - }); - - describe('Error', () => { - generateTests({ - toWrite: Buffer.from('-ERR\r\n'), - returnStringsAsBuffers: false, - replies: [[new ErrorReply('ERR')]] - }); - }); - - describe('Integer', () => { - describe('-1', () => { - generateTests({ - toWrite: Buffer.from(':-1\r\n'), - returnStringsAsBuffers: false, - replies: [[-1]] - }); - }); - - describe('0', () => { - generateTests({ - toWrite: Buffer.from(':0\r\n'), - returnStringsAsBuffers: false, - replies: [[0]] - }); - }); - }); - - describe('Bulk String', () => { - describe('null', () => { - generateTests({ - toWrite: Buffer.from('$-1\r\n'), - returnStringsAsBuffers: false, - replies: [[null]] - }); - }); - - describe('as strings', () => { - generateTests({ - toWrite: Buffer.from('$2\r\naa\r\n'), - returnStringsAsBuffers: false, - replies: [['aa']] - }); - }); - - describe('as buffers', () => { - generateTests({ - toWrite: Buffer.from('$2\r\naa\r\n'), - returnStringsAsBuffers: true, - replies: [[Buffer.from('aa')]] - }); - }); - }); - - describe('Array', () => { - describe('null', () => { - generateTests({ - toWrite: Buffer.from('*-1\r\n'), - returnStringsAsBuffers: false, - replies: [[null]] - }); - }); - - const arrayBuffer = Buffer.from( - '*5\r\n' + - '+OK\r\n' + - '-ERR\r\n' + - ':0\r\n' + - '$1\r\na\r\n' + - '*0\r\n' - ); - - describe('as strings', () => { - generateTests({ - toWrite: arrayBuffer, - returnStringsAsBuffers: false, - replies: [[[ - 'OK', - new ErrorReply('ERR'), - 0, - 'a', - [] - ]]] - }); - }); - - describe('as buffers', () => { - generateTests({ - toWrite: arrayBuffer, - returnStringsAsBuffers: true, - replies: [[[ - Buffer.from('OK'), - new ErrorReply('ERR'), - 0, - Buffer.from('a'), - [] - ]]] - }); - }); - }); -}); diff --git a/packages/client/lib/client/RESP2/decoder.ts b/packages/client/lib/client/RESP2/decoder.ts deleted file mode 100644 index 525f118bf30..00000000000 --- a/packages/client/lib/client/RESP2/decoder.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { ErrorReply } from '../../errors'; -import { Composer } from './composers/interface'; -import BufferComposer from './composers/buffer'; -import StringComposer from './composers/string'; - -// RESP2 specification -// https://redis.io/topics/protocol - -enum Types { - SIMPLE_STRING = 43, // + - ERROR = 45, // - - INTEGER = 58, // : - BULK_STRING = 36, // $ - ARRAY = 42 // * -} - -enum ASCII { - CR = 13, // \r - ZERO = 48, - MINUS = 45 -} - -export type Reply = string | Buffer | ErrorReply | number | null | Array; - -type ArrayReply = Array | null; - -export type ReturnStringsAsBuffers = () => boolean; - -interface RESP2Options { - returnStringsAsBuffers: ReturnStringsAsBuffers; - onReply(reply: Reply): unknown; -} - -interface ArrayInProcess { - array: Array; - pushCounter: number; -} - -// Using TypeScript `private` and not the build-in `#` to avoid __classPrivateFieldGet and __classPrivateFieldSet - -export default class RESP2Decoder { - constructor(private options: RESP2Options) {} - - private cursor = 0; - - private type?: Types; - - private bufferComposer = new BufferComposer(); - - private stringComposer = new StringComposer(); - - private currentStringComposer: BufferComposer | StringComposer = this.stringComposer; - - reset() { - this.cursor = 0; - this.type = undefined; - this.bufferComposer.reset(); - this.stringComposer.reset(); - this.currentStringComposer = this.stringComposer; - } - - write(chunk: Buffer): void { - while (this.cursor < chunk.length) { - if (!this.type) { - this.currentStringComposer = this.options.returnStringsAsBuffers() ? - this.bufferComposer : - this.stringComposer; - - this.type = chunk[this.cursor]; - if (++this.cursor >= chunk.length) break; - } - - const reply = this.parseType(chunk, this.type); - if (reply === undefined) break; - - this.type = undefined; - this.options.onReply(reply); - } - - this.cursor -= chunk.length; - } - - private parseType(chunk: Buffer, type: Types, arraysToKeep?: number): Reply | undefined { - switch (type) { - case Types.SIMPLE_STRING: - return this.parseSimpleString(chunk); - - case Types.ERROR: - return this.parseError(chunk); - - case Types.INTEGER: - return this.parseInteger(chunk); - - case Types.BULK_STRING: - return this.parseBulkString(chunk); - - case Types.ARRAY: - return this.parseArray(chunk, arraysToKeep); - } - } - - private compose< - C extends Composer, - T = C extends Composer ? TT : never - >( - chunk: Buffer, - composer: C - ): T | undefined { - for (let i = this.cursor; i < chunk.length; i++) { - if (chunk[i] === ASCII.CR) { - const reply = composer.end( - chunk.subarray(this.cursor, i) - ); - this.cursor = i + 2; - return reply; - } - } - - const toWrite = chunk.subarray(this.cursor); - composer.write(toWrite); - this.cursor = chunk.length; - } - - private parseSimpleString(chunk: Buffer): string | Buffer | undefined { - return this.compose(chunk, this.currentStringComposer); - } - - private parseError(chunk: Buffer): ErrorReply | undefined { - const message = this.compose(chunk, this.stringComposer); - if (message !== undefined) { - return new ErrorReply(message); - } - } - - private integer = 0; - - private isNegativeInteger?: boolean; - - private parseInteger(chunk: Buffer): number | undefined { - if (this.isNegativeInteger === undefined) { - this.isNegativeInteger = chunk[this.cursor] === ASCII.MINUS; - if (this.isNegativeInteger && ++this.cursor === chunk.length) return; - } - - do { - const byte = chunk[this.cursor]; - if (byte === ASCII.CR) { - const integer = this.isNegativeInteger ? -this.integer : this.integer; - this.integer = 0; - this.isNegativeInteger = undefined; - this.cursor += 2; - return integer; - } - - this.integer = this.integer * 10 + byte - ASCII.ZERO; - } while (++this.cursor < chunk.length); - } - - private bulkStringRemainingLength?: number; - - private parseBulkString(chunk: Buffer): string | Buffer | null | undefined { - if (this.bulkStringRemainingLength === undefined) { - const length = this.parseInteger(chunk); - if (length === undefined) return; - if (length === -1) return null; - - this.bulkStringRemainingLength = length; - - if (this.cursor >= chunk.length) return; - } - - const end = this.cursor + this.bulkStringRemainingLength; - if (chunk.length >= end) { - const reply = this.currentStringComposer.end( - chunk.subarray(this.cursor, end) - ); - this.bulkStringRemainingLength = undefined; - this.cursor = end + 2; - return reply; - } - - const toWrite = chunk.subarray(this.cursor); - this.currentStringComposer.write(toWrite); - this.bulkStringRemainingLength -= toWrite.length; - this.cursor = chunk.length; - } - - private arraysInProcess: Array = []; - - private initializeArray = false; - - private arrayItemType?: Types; - - private parseArray(chunk: Buffer, arraysToKeep = 0): ArrayReply | undefined { - if (this.initializeArray || this.arraysInProcess.length === arraysToKeep) { - const length = this.parseInteger(chunk); - if (length === undefined) { - this.initializeArray = true; - return undefined; - } - - this.initializeArray = false; - this.arrayItemType = undefined; - - if (length === -1) { - return this.returnArrayReply(null, arraysToKeep, chunk); - } else if (length === 0) { - return this.returnArrayReply([], arraysToKeep, chunk); - } - - this.arraysInProcess.push({ - array: new Array(length), - pushCounter: 0 - }); - } - - while (this.cursor < chunk.length) { - if (!this.arrayItemType) { - this.arrayItemType = chunk[this.cursor]; - - if (++this.cursor >= chunk.length) break; - } - - const item = this.parseType( - chunk, - this.arrayItemType, - arraysToKeep + 1 - ); - if (item === undefined) break; - - this.arrayItemType = undefined; - - const reply = this.pushArrayItem(item, arraysToKeep); - if (reply !== undefined) return reply; - } - } - - private returnArrayReply(reply: ArrayReply, arraysToKeep: number, chunk?: Buffer): ArrayReply | undefined { - if (this.arraysInProcess.length <= arraysToKeep) return reply; - - return this.pushArrayItem(reply, arraysToKeep, chunk); - } - - private pushArrayItem(item: Reply, arraysToKeep: number, chunk?: Buffer): ArrayReply | undefined { - const to = this.arraysInProcess[this.arraysInProcess.length - 1]!; - to.array[to.pushCounter] = item; - if (++to.pushCounter === to.array.length) { - return this.returnArrayReply( - this.arraysInProcess.pop()!.array, - arraysToKeep, - chunk - ); - } else if (chunk && chunk.length > this.cursor) { - return this.parseArray(chunk, arraysToKeep); - } - } -} diff --git a/packages/client/lib/client/RESP2/encoder.spec.ts b/packages/client/lib/client/RESP2/encoder.spec.ts deleted file mode 100644 index 486259472a4..00000000000 --- a/packages/client/lib/client/RESP2/encoder.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { strict as assert } from 'assert'; -import { describe } from 'mocha'; -import encodeCommand from './encoder'; - -describe('RESP2 Encoder', () => { - it('1 byte', () => { - assert.deepEqual( - encodeCommand(['a', 'z']), - ['*2\r\n$1\r\na\r\n$1\r\nz\r\n'] - ); - }); - - it('2 bytes', () => { - assert.deepEqual( - encodeCommand(['א', 'ת']), - ['*2\r\n$2\r\nא\r\n$2\r\nת\r\n'] - ); - }); - - it('4 bytes', () => { - assert.deepEqual( - [...encodeCommand(['🐣', '🐤'])], - ['*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n'] - ); - }); - - it('buffer', () => { - assert.deepEqual( - encodeCommand([Buffer.from('string')]), - ['*1\r\n$6\r\n', Buffer.from('string'), '\r\n'] - ); - }); -}); diff --git a/packages/client/lib/client/RESP2/encoder.ts b/packages/client/lib/client/RESP2/encoder.ts deleted file mode 100644 index 217fbc714bb..00000000000 --- a/packages/client/lib/client/RESP2/encoder.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { RedisCommandArgument, RedisCommandArguments } from '../../commands'; - -const CRLF = '\r\n'; - -export default function encodeCommand(args: RedisCommandArguments): Array { - const toWrite: Array = []; - - let strings = '*' + args.length + CRLF; - - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - if (typeof arg === 'string') { - strings += '$' + Buffer.byteLength(arg) + CRLF + arg + CRLF; - } else if (arg instanceof Buffer) { - toWrite.push( - strings + '$' + arg.length.toString() + CRLF, - arg - ); - strings = CRLF; - } else { - throw new TypeError('Invalid argument type'); - } - } - - toWrite.push(strings); - - return toWrite; -} diff --git a/packages/client/lib/client/commands-queue.ts b/packages/client/lib/client/commands-queue.ts index 7fffed86580..69ba57425a6 100644 --- a/packages/client/lib/client/commands-queue.ts +++ b/packages/client/lib/client/commands-queue.ts @@ -1,263 +1,319 @@ import * as LinkedList from 'yallist'; -import { AbortError, ErrorReply } from '../errors'; -import { RedisCommandArguments, RedisCommandRawReply } from '../commands'; -import RESP2Decoder from './RESP2/decoder'; -import encodeCommand from './RESP2/encoder'; +import encodeCommand from '../RESP/encoder'; +import { Decoder, PUSH_FLAGS, RESP_TYPES } from '../RESP/decoder'; +import { CommandArguments, Flags, ReplyUnion, RespVersions } from '../RESP/types'; import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub'; +import { AbortError, ErrorReply } from '../errors'; +import { EventEmitter } from 'stream'; export interface QueueCommandOptions { - asap?: boolean; - chainId?: symbol; - signal?: AbortSignal; - returnBuffers?: boolean; + asap?: boolean; + chainId?: symbol; + signal?: AbortSignal; + flags?: Flags; } export interface CommandWaitingToBeSent extends CommandWaitingForReply { - args: RedisCommandArguments; - chainId?: symbol; - abort?: { - signal: AbortSignal; - listener(): void; - }; + args: CommandArguments; + chainId?: symbol; + removeAbortListener?(): void; } interface CommandWaitingForReply { - resolve(reply?: unknown): void; - reject(err: unknown): void; - channelsCounter?: number; - returnBuffers?: boolean; + resolve(reply?: unknown): void; + reject(err: unknown): void; + channelsCounter?: number; + flags?: Flags; } +export type OnShardedChannelMoved = (channel: string, listeners: ChannelListeners) => void; + const PONG = Buffer.from('pong'); -export type OnShardedChannelMoved = (channel: string, listeners: ChannelListeners) => void; +const RESP2_PUSH_FLAGS = { + ...PUSH_FLAGS, + [RESP_TYPES.SIMPLE_STRING]: Buffer +}; export default class RedisCommandsQueue { - static #flushQueue(queue: LinkedList, err: Error): void { - while (queue.length) { - queue.shift()!.reject(err); - } + private readonly _maxLength: number | null | undefined; + private readonly _waitingToBeSent = new LinkedList(); + private readonly _waitingForReply = new LinkedList(); + private readonly _onShardedChannelMoved: OnShardedChannelMoved; + + private readonly _pubSub = new PubSub(); + + get isPubSubActive() { + return this._pubSub.isActive; + } + + private _chainInExecution: symbol | undefined; + + decoder: Decoder; + + constructor( + respVersion: RespVersions | null | undefined, + maxLength: number | null | undefined, + onShardedChannelMoved: EventEmitter['emit'] + ) { + this.decoder = this._initiateDecoder(respVersion); + this._maxLength = maxLength; + this._onShardedChannelMoved = onShardedChannelMoved; + } + + private _initiateDecoder(respVersion: RespVersions | null | undefined) { + return respVersion === 3 ? + this._initiateResp3Decoder() : + this._initiateResp2Decoder(); + } + + private _onReply(reply: ReplyUnion) { + this._waitingForReply.shift()!.resolve(reply); + } + + private _onErrorReply(err: ErrorReply) { + this._waitingForReply.shift()!.reject(err); + } + + private _onPush(push: Array) { + // TODO: type + if (this._pubSub.handleMessageReply(push)) return true; + + const isShardedUnsubscribe = PubSub.isShardedUnsubscribe(push); + if (isShardedUnsubscribe && !this._waitingForReply.length) { + const channel = push[1].toString(); + this._onShardedChannelMoved( + channel, + this._pubSub.removeShardedListeners(channel) + ); + return true; + } else if (isShardedUnsubscribe || PubSub.isStatusReply(push)) { + const head = this._waitingForReply.head!.value; + if ( + (Number.isNaN(head.channelsCounter!) && push[2] === 0) || + --head.channelsCounter! === 0 + ) { + this._waitingForReply.shift()!.resolve(); + } + return true; } + } - readonly #maxLength: number | null | undefined; - readonly #waitingToBeSent = new LinkedList(); - readonly #waitingForReply = new LinkedList(); - readonly #onShardedChannelMoved: OnShardedChannelMoved; - - readonly #pubSub = new PubSub(); - - get isPubSubActive() { - return this.#pubSub.isActive; - } + private _getFlags() { + return this._waitingForReply.head!.value.flags ?? {}; + } - #chainInExecution: symbol | undefined; + private _initiateResp3Decoder() { + return new Decoder({ + onReply: reply => this._onReply(reply), + onErrorReply: err => this._onErrorReply(err), + onPush: push => { + if (!this._onPush(push)) { - #decoder = new RESP2Decoder({ - returnStringsAsBuffers: () => { - return !!this.#waitingForReply.head?.value.returnBuffers || - this.#pubSub.isActive; - }, - onReply: reply => { - if (this.#pubSub.isActive && Array.isArray(reply)) { - if (this.#pubSub.handleMessageReply(reply as Array)) return; - - const isShardedUnsubscribe = PubSub.isShardedUnsubscribe(reply as Array); - if (isShardedUnsubscribe && !this.#waitingForReply.length) { - const channel = (reply[1] as Buffer).toString(); - this.#onShardedChannelMoved( - channel, - this.#pubSub.removeShardedListeners(channel) - ); - return; - } else if (isShardedUnsubscribe || PubSub.isStatusReply(reply as Array)) { - const head = this.#waitingForReply.head!.value; - if ( - (Number.isNaN(head.channelsCounter!) && reply[2] === 0) || - --head.channelsCounter! === 0 - ) { - this.#waitingForReply.shift()!.resolve(); - } - return; - } - if (PONG.equals(reply[0] as Buffer)) { - const { resolve, returnBuffers } = this.#waitingForReply.shift()!, - buffer = ((reply[1] as Buffer).length === 0 ? reply[0] : reply[1]) as Buffer; - resolve(returnBuffers ? buffer : buffer.toString()); - return; - } - } - - const { resolve, reject } = this.#waitingForReply.shift()!; - if (reply instanceof ErrorReply) { - reject(reply); - } else { - resolve(reply); - } } + }, + getFlags: () => this._getFlags() }); - - constructor( - maxLength: number | null | undefined, - onShardedChannelMoved: OnShardedChannelMoved - ) { - this.#maxLength = maxLength; - this.#onShardedChannelMoved = onShardedChannelMoved; - } - - addCommand(args: RedisCommandArguments, options?: QueueCommandOptions): Promise { - if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) { - return Promise.reject(new Error('The queue is full')); - } else if (options?.signal?.aborted) { - return Promise.reject(new AbortError()); + } + + private _initiateResp2Decoder() { + return new Decoder({ + onReply: reply => { + if (this._pubSub.isActive && Array.isArray(reply)) { + if (this._onPush(reply)) return; + + if (PONG.equals(reply[0] as Buffer)) { + const { resolve, flags } = this._waitingForReply.shift()!, + buffer = ((reply[1] as Buffer).length === 0 ? reply[0] : reply[1]) as Buffer; + resolve(flags?.[RESP_TYPES.SIMPLE_STRING] === Buffer ? buffer : buffer.toString()); + return; + } } - return new Promise((resolve, reject) => { - const node = new LinkedList.Node({ - args, - chainId: options?.chainId, - returnBuffers: options?.returnBuffers, - resolve, - reject - }); - - if (options?.signal) { - const listener = () => { - this.#waitingToBeSent.removeNode(node); - node.value.reject(new AbortError()); - }; - node.value.abort = { - signal: options.signal, - listener - }; - // AbortSignal type is incorrent - (options.signal as any).addEventListener('abort', listener, { - once: true - }); - } - - if (options?.asap) { - this.#waitingToBeSent.unshiftNode(node); - } else { - this.#waitingToBeSent.pushNode(node); - } - }); - } - - subscribe( - type: PubSubType, - channels: string | Array, - listener: PubSubListener, - returnBuffers?: T - ) { - return this.#pushPubSubCommand( - this.#pubSub.subscribe(type, channels, listener, returnBuffers) - ); - } - - unsubscribe( - type: PubSubType, - channels?: string | Array, - listener?: PubSubListener, - returnBuffers?: T - ) { - return this.#pushPubSubCommand( - this.#pubSub.unsubscribe(type, channels, listener, returnBuffers) - ); - } - - resubscribe(): Promise | undefined { - const commands = this.#pubSub.resubscribe(); - if (!commands.length) return; - - return Promise.all( - commands.map(command => this.#pushPubSubCommand(command)) - ); - } - - extendPubSubChannelListeners( - type: PubSubType, - channel: string, - listeners: ChannelListeners - ) { - return this.#pushPubSubCommand( - this.#pubSub.extendChannelListeners(type, channel, listeners) - ); - } + this._onReply(reply); + }, + onErrorReply: err => this._onErrorReply(err), + // PUSH type does not exist in RESP2 + // PubSub is handled in onReply + // @ts-expect-error + onPush: undefined, + getFlags: () => { + // PubSub push is an Array in RESP2 + return this._pubSub.isActive ? + RESP2_PUSH_FLAGS : + this._getFlags(); + } + }); + } - extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { - return this.#pushPubSubCommand( - this.#pubSub.extendTypeListeners(type, listeners) - ); + addCommand(args: CommandArguments, options?: QueueCommandOptions): Promise { + if (this._maxLength && this._waitingToBeSent.length + this._waitingForReply.length >= this._maxLength) { + return Promise.reject(new Error('The queue is full')); + } else if (options?.signal?.aborted) { + return Promise.reject(new AbortError()); } - getPubSubListeners(type: PubSubType) { - return this.#pubSub.getTypeListeners(type); + return new Promise((resolve, reject) => { + const node = new LinkedList.Node({ + args, + chainId: options?.chainId, + flags: options?.flags, + resolve, + reject + }); + + if (options?.signal) { + const listener = () => { + this._waitingToBeSent.removeNode(node); + node.value.reject(new AbortError()); + }; + + node.value.removeAbortListener = () => options.signal?.removeEventListener('abort', listener); + + options.signal.addEventListener('abort', listener, { once: true }); + } + + if (options?.asap) { + this._waitingToBeSent.unshiftNode(node); + } else { + this._waitingToBeSent.pushNode(node); + } + }); + } + + subscribe( + type: PubSubType, + channels: string | Array, + listener: PubSubListener, + returnBuffers?: T + ) { + return this._pushPubSubCommand( + this._pubSub.subscribe(type, channels, listener, returnBuffers) + ); + } + + unsubscribe( + type: PubSubType, + channels?: string | Array, + listener?: PubSubListener, + returnBuffers?: T + ) { + return this._pushPubSubCommand( + this._pubSub.unsubscribe(type, channels, listener, returnBuffers) + ); + } + + resubscribe(): Promise | undefined { + const commands = this._pubSub.resubscribe(); + if (!commands.length) return; + + return Promise.all( + commands.map(command => this._pushPubSubCommand(command)) + ); + } + + extendPubSubChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + return this._pushPubSubCommand( + this._pubSub.extendChannelListeners(type, channel, listeners) + ); + } + + extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { + return this._pushPubSubCommand( + this._pubSub.extendTypeListeners(type, listeners) + ); + } + + getPubSubListeners(type: PubSubType) { + return this._pubSub.getTypeListeners(type); + } + + private _pushPubSubCommand(command: PubSubCommand) { + if (command === undefined) return; + + return new Promise((resolve, reject) => { + this._waitingToBeSent.push({ + args: command.args, + channelsCounter: command.channelsCounter, + flags: PUSH_FLAGS, + resolve: () => { + command.resolve(); + resolve(); + }, + reject: err => { + command.reject?.(); + reject(err); + } + }); + }); + } + + getCommandToSend(): CommandArguments | undefined { + const toSend = this._waitingToBeSent.shift(); + if (!toSend) return; + + let encoded: CommandArguments; + try { + encoded = encodeCommand(toSend.args); + } catch (err) { + toSend.reject(err); + return; } - #pushPubSubCommand(command: PubSubCommand) { - if (command === undefined) return; - - return new Promise((resolve, reject) => { - this.#waitingToBeSent.push({ - args: command.args, - channelsCounter: command.channelsCounter, - returnBuffers: true, - resolve: () => { - command.resolve(); - resolve(); - }, - reject: err => { - command.reject?.(); - reject(err); - } - }); - }); + // TODO + // reuse `toSend` + (toSend.args as any) = undefined; + if (toSend.removeAbortListener) { + toSend.removeAbortListener(); + (toSend.removeAbortListener as any) = undefined; } - - getCommandToSend(): RedisCommandArguments | undefined { - const toSend = this.#waitingToBeSent.shift(); - if (!toSend) return; - - let encoded: RedisCommandArguments; - try { - encoded = encodeCommand(toSend.args); - } catch (err) { - toSend.reject(err); - return; - } - - this.#waitingForReply.push({ - resolve: toSend.resolve, - reject: toSend.reject, - channelsCounter: toSend.channelsCounter, - returnBuffers: toSend.returnBuffers - }); - this.#chainInExecution = toSend.chainId; - return encoded; + this._waitingForReply.push(toSend); + this._chainInExecution = toSend.chainId; + return encoded; + } + + #flushWaitingForReply(err: Error): void { + while (this._waitingForReply.head) { + this._waitingForReply.shift()!.reject(err); } + } - onReplyChunk(chunk: Buffer): void { - this.#decoder.write(chunk); - } + static #flushWaitingToBeSent(command: CommandWaitingToBeSent, err: Error) { + command.removeAbortListener?.(); + command.reject(err); + } - flushWaitingForReply(err: Error): void { - this.#decoder.reset(); - this.#pubSub.reset(); - RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); + flushWaitingForReply(err: Error): void { + this.decoder.reset(); + this._pubSub.reset(); - if (!this.#chainInExecution) return; + this.#flushWaitingForReply(err); - while (this.#waitingToBeSent.head?.value.chainId === this.#chainInExecution) { - this.#waitingToBeSent.shift(); - } + if (!this._chainInExecution) return; - this.#chainInExecution = undefined; + while (this._waitingToBeSent.head?.value.chainId === this._chainInExecution) { + RedisCommandsQueue.#flushWaitingToBeSent( + this._waitingToBeSent.shift()!, + err + ); } - flushAll(err: Error): void { - this.#decoder.reset(); - this.#pubSub.reset(); - RedisCommandsQueue.#flushQueue(this.#waitingForReply, err); - RedisCommandsQueue.#flushQueue(this.#waitingToBeSent, err); + this._chainInExecution = undefined; + } + + flushAll(err: Error): void { + this.decoder.reset(); + this._pubSub.reset(); + this.#flushWaitingForReply(err); + while (this._waitingToBeSent.head) { + RedisCommandsQueue.#flushWaitingToBeSent( + this._waitingToBeSent.shift()!, + err + ); } + } } diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts deleted file mode 100644 index 2605962432a..00000000000 --- a/packages/client/lib/client/commands.ts +++ /dev/null @@ -1,359 +0,0 @@ -import CLUSTER_COMMANDS from '../cluster/commands'; -import * as ACL_CAT from '../commands/ACL_CAT'; -import * as ACL_DELUSER from '../commands/ACL_DELUSER'; -import * as ACL_DRYRUN from '../commands/ACL_DRYRUN'; -import * as ACL_GENPASS from '../commands/ACL_GENPASS'; -import * as ACL_GETUSER from '../commands/ACL_GETUSER'; -import * as ACL_LIST from '../commands/ACL_LIST'; -import * as ACL_LOAD from '../commands/ACL_LOAD'; -import * as ACL_LOG_RESET from '../commands/ACL_LOG_RESET'; -import * as ACL_LOG from '../commands/ACL_LOG'; -import * as ACL_SAVE from '../commands/ACL_SAVE'; -import * as ACL_SETUSER from '../commands/ACL_SETUSER'; -import * as ACL_USERS from '../commands/ACL_USERS'; -import * as ACL_WHOAMI from '../commands/ACL_WHOAMI'; -import * as ASKING from '../commands/ASKING'; -import * as AUTH from '../commands/AUTH'; -import * as BGREWRITEAOF from '../commands/BGREWRITEAOF'; -import * as BGSAVE from '../commands/BGSAVE'; -import * as CLIENT_CACHING from '../commands/CLIENT_CACHING'; -import * as CLIENT_GETNAME from '../commands/CLIENT_GETNAME'; -import * as CLIENT_GETREDIR from '../commands/CLIENT_GETREDIR'; -import * as CLIENT_ID from '../commands/CLIENT_ID'; -import * as CLIENT_KILL from '../commands/CLIENT_KILL'; -import * as CLIENT_LIST from '../commands/CLIENT_LIST'; -import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; -import * as CLIENT_PAUSE from '../commands/CLIENT_PAUSE'; -import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; -import * as CLIENT_TRACKING from '../commands/CLIENT_TRACKING'; -import * as CLIENT_TRACKINGINFO from '../commands/CLIENT_TRACKINGINFO'; -import * as CLIENT_UNPAUSE from '../commands/CLIENT_UNPAUSE'; -import * as CLIENT_INFO from '../commands/CLIENT_INFO'; -import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; -import * as CLUSTER_ADDSLOTSRANGE from '../commands/CLUSTER_ADDSLOTSRANGE'; -import * as CLUSTER_BUMPEPOCH from '../commands/CLUSTER_BUMPEPOCH'; -import * as CLUSTER_COUNT_FAILURE_REPORTS from '../commands/CLUSTER_COUNT-FAILURE-REPORTS'; -import * as CLUSTER_COUNTKEYSINSLOT from '../commands/CLUSTER_COUNTKEYSINSLOT'; -import * as CLUSTER_DELSLOTS from '../commands/CLUSTER_DELSLOTS'; -import * as CLUSTER_DELSLOTSRANGE from '../commands/CLUSTER_DELSLOTSRANGE'; -import * as CLUSTER_FAILOVER from '../commands/CLUSTER_FAILOVER'; -import * as CLUSTER_FLUSHSLOTS from '../commands/CLUSTER_FLUSHSLOTS'; -import * as CLUSTER_FORGET from '../commands/CLUSTER_FORGET'; -import * as CLUSTER_GETKEYSINSLOT from '../commands/CLUSTER_GETKEYSINSLOT'; -import * as CLUSTER_INFO from '../commands/CLUSTER_INFO'; -import * as CLUSTER_KEYSLOT from '../commands/CLUSTER_KEYSLOT'; -import * as CLUSTER_LINKS from '../commands/CLUSTER_LINKS'; -import * as CLUSTER_MEET from '../commands/CLUSTER_MEET'; -import * as CLUSTER_MYID from '../commands/CLUSTER_MYID'; -import * as CLUSTER_NODES from '../commands/CLUSTER_NODES'; -import * as CLUSTER_REPLICAS from '../commands/CLUSTER_REPLICAS'; -import * as CLUSTER_REPLICATE from '../commands/CLUSTER_REPLICATE'; -import * as CLUSTER_RESET from '../commands/CLUSTER_RESET'; -import * as CLUSTER_SAVECONFIG from '../commands/CLUSTER_SAVECONFIG'; -import * as CLUSTER_SET_CONFIG_EPOCH from '../commands/CLUSTER_SET-CONFIG-EPOCH'; -import * as CLUSTER_SETSLOT from '../commands/CLUSTER_SETSLOT'; -import * as CLUSTER_SLOTS from '../commands/CLUSTER_SLOTS'; -import * as COMMAND_COUNT from '../commands/COMMAND_COUNT'; -import * as COMMAND_GETKEYS from '../commands/COMMAND_GETKEYS'; -import * as COMMAND_GETKEYSANDFLAGS from '../commands/COMMAND_GETKEYSANDFLAGS'; -import * as COMMAND_INFO from '../commands/COMMAND_INFO'; -import * as COMMAND_LIST from '../commands/COMMAND_LIST'; -import * as COMMAND from '../commands/COMMAND'; -import * as CONFIG_GET from '../commands/CONFIG_GET'; -import * as CONFIG_RESETASTAT from '../commands/CONFIG_RESETSTAT'; -import * as CONFIG_REWRITE from '../commands/CONFIG_REWRITE'; -import * as CONFIG_SET from '../commands/CONFIG_SET'; -import * as DBSIZE from '../commands/DBSIZE'; -import * as DISCARD from '../commands/DISCARD'; -import * as ECHO from '../commands/ECHO'; -import * as FAILOVER from '../commands/FAILOVER'; -import * as FLUSHALL from '../commands/FLUSHALL'; -import * as FLUSHDB from '../commands/FLUSHDB'; -import * as FUNCTION_DELETE from '../commands/FUNCTION_DELETE'; -import * as FUNCTION_DUMP from '../commands/FUNCTION_DUMP'; -import * as FUNCTION_FLUSH from '../commands/FUNCTION_FLUSH'; -import * as FUNCTION_KILL from '../commands/FUNCTION_KILL'; -import * as FUNCTION_LIST_WITHCODE from '../commands/FUNCTION_LIST_WITHCODE'; -import * as FUNCTION_LIST from '../commands/FUNCTION_LIST'; -import * as FUNCTION_LOAD from '../commands/FUNCTION_LOAD'; -import * as FUNCTION_RESTORE from '../commands/FUNCTION_RESTORE'; -import * as FUNCTION_STATS from '../commands/FUNCTION_STATS'; -import * as HELLO from '../commands/HELLO'; -import * as INFO from '../commands/INFO'; -import * as KEYS from '../commands/KEYS'; -import * as LASTSAVE from '../commands/LASTSAVE'; -import * as LATENCY_DOCTOR from '../commands/LATENCY_DOCTOR'; -import * as LATENCY_GRAPH from '../commands/LATENCY_GRAPH'; -import * as LOLWUT from '../commands/LOLWUT'; -import * as MEMORY_DOCTOR from '../commands/MEMORY_DOCTOR'; -import * as MEMORY_MALLOC_STATS from '../commands/MEMORY_MALLOC-STATS'; -import * as MEMORY_PURGE from '../commands/MEMORY_PURGE'; -import * as MEMORY_STATS from '../commands/MEMORY_STATS'; -import * as MEMORY_USAGE from '../commands/MEMORY_USAGE'; -import * as MODULE_LIST from '../commands/MODULE_LIST'; -import * as MODULE_LOAD from '../commands/MODULE_LOAD'; -import * as MODULE_UNLOAD from '../commands/MODULE_UNLOAD'; -import * as MOVE from '../commands/MOVE'; -import * as PING from '../commands/PING'; -import * as PUBSUB_CHANNELS from '../commands/PUBSUB_CHANNELS'; -import * as PUBSUB_NUMPAT from '../commands/PUBSUB_NUMPAT'; -import * as PUBSUB_NUMSUB from '../commands/PUBSUB_NUMSUB'; -import * as PUBSUB_SHARDCHANNELS from '../commands/PUBSUB_SHARDCHANNELS'; -import * as RANDOMKEY from '../commands/RANDOMKEY'; -import * as READONLY from '../commands/READONLY'; -import * as READWRITE from '../commands/READWRITE'; -import * as REPLICAOF from '../commands/REPLICAOF'; -import * as RESTORE_ASKING from '../commands/RESTORE-ASKING'; -import * as ROLE from '../commands/ROLE'; -import * as SAVE from '../commands/SAVE'; -import * as SCAN from '../commands/SCAN'; -import * as SCRIPT_DEBUG from '../commands/SCRIPT_DEBUG'; -import * as SCRIPT_EXISTS from '../commands/SCRIPT_EXISTS'; -import * as SCRIPT_FLUSH from '../commands/SCRIPT_FLUSH'; -import * as SCRIPT_KILL from '../commands/SCRIPT_KILL'; -import * as SCRIPT_LOAD from '../commands/SCRIPT_LOAD'; -import * as SHUTDOWN from '../commands/SHUTDOWN'; -import * as SWAPDB from '../commands/SWAPDB'; -import * as TIME from '../commands/TIME'; -import * as UNWATCH from '../commands/UNWATCH'; -import * as WAIT from '../commands/WAIT'; - -export default { - ...CLUSTER_COMMANDS, - ACL_CAT, - aclCat: ACL_CAT, - ACL_DELUSER, - aclDelUser: ACL_DELUSER, - ACL_DRYRUN, - aclDryRun: ACL_DRYRUN, - ACL_GENPASS, - aclGenPass: ACL_GENPASS, - ACL_GETUSER, - aclGetUser: ACL_GETUSER, - ACL_LIST, - aclList: ACL_LIST, - ACL_LOAD, - aclLoad: ACL_LOAD, - ACL_LOG_RESET, - aclLogReset: ACL_LOG_RESET, - ACL_LOG, - aclLog: ACL_LOG, - ACL_SAVE, - aclSave: ACL_SAVE, - ACL_SETUSER, - aclSetUser: ACL_SETUSER, - ACL_USERS, - aclUsers: ACL_USERS, - ACL_WHOAMI, - aclWhoAmI: ACL_WHOAMI, - ASKING, - asking: ASKING, - AUTH, - auth: AUTH, - BGREWRITEAOF, - bgRewriteAof: BGREWRITEAOF, - BGSAVE, - bgSave: BGSAVE, - CLIENT_CACHING, - clientCaching: CLIENT_CACHING, - CLIENT_GETNAME, - clientGetName: CLIENT_GETNAME, - CLIENT_GETREDIR, - clientGetRedir: CLIENT_GETREDIR, - CLIENT_ID, - clientId: CLIENT_ID, - CLIENT_KILL, - clientKill: CLIENT_KILL, - 'CLIENT_NO-EVICT': CLIENT_NO_EVICT, - clientNoEvict: CLIENT_NO_EVICT, - CLIENT_LIST, - clientList: CLIENT_LIST, - CLIENT_PAUSE, - clientPause: CLIENT_PAUSE, - CLIENT_SETNAME, - clientSetName: CLIENT_SETNAME, - CLIENT_TRACKING, - clientTracking: CLIENT_TRACKING, - CLIENT_TRACKINGINFO, - clientTrackingInfo: CLIENT_TRACKINGINFO, - CLIENT_UNPAUSE, - clientUnpause: CLIENT_UNPAUSE, - CLIENT_INFO, - clientInfo: CLIENT_INFO, - CLUSTER_ADDSLOTS, - clusterAddSlots: CLUSTER_ADDSLOTS, - CLUSTER_ADDSLOTSRANGE, - clusterAddSlotsRange: CLUSTER_ADDSLOTSRANGE, - CLUSTER_BUMPEPOCH, - clusterBumpEpoch: CLUSTER_BUMPEPOCH, - CLUSTER_COUNT_FAILURE_REPORTS, - clusterCountFailureReports: CLUSTER_COUNT_FAILURE_REPORTS, - CLUSTER_COUNTKEYSINSLOT, - clusterCountKeysInSlot: CLUSTER_COUNTKEYSINSLOT, - CLUSTER_DELSLOTS, - clusterDelSlots: CLUSTER_DELSLOTS, - CLUSTER_DELSLOTSRANGE, - clusterDelSlotsRange: CLUSTER_DELSLOTSRANGE, - CLUSTER_FAILOVER, - clusterFailover: CLUSTER_FAILOVER, - CLUSTER_FLUSHSLOTS, - clusterFlushSlots: CLUSTER_FLUSHSLOTS, - CLUSTER_FORGET, - clusterForget: CLUSTER_FORGET, - CLUSTER_GETKEYSINSLOT, - clusterGetKeysInSlot: CLUSTER_GETKEYSINSLOT, - CLUSTER_INFO, - clusterInfo: CLUSTER_INFO, - CLUSTER_KEYSLOT, - clusterKeySlot: CLUSTER_KEYSLOT, - CLUSTER_LINKS, - clusterLinks: CLUSTER_LINKS, - CLUSTER_MEET, - clusterMeet: CLUSTER_MEET, - CLUSTER_MYID, - clusterMyId: CLUSTER_MYID, - CLUSTER_NODES, - clusterNodes: CLUSTER_NODES, - CLUSTER_REPLICAS, - clusterReplicas: CLUSTER_REPLICAS, - CLUSTER_REPLICATE, - clusterReplicate: CLUSTER_REPLICATE, - CLUSTER_RESET, - clusterReset: CLUSTER_RESET, - CLUSTER_SAVECONFIG, - clusterSaveConfig: CLUSTER_SAVECONFIG, - CLUSTER_SET_CONFIG_EPOCH, - clusterSetConfigEpoch: CLUSTER_SET_CONFIG_EPOCH, - CLUSTER_SETSLOT, - clusterSetSlot: CLUSTER_SETSLOT, - CLUSTER_SLOTS, - clusterSlots: CLUSTER_SLOTS, - COMMAND_COUNT, - commandCount: COMMAND_COUNT, - COMMAND_GETKEYS, - commandGetKeys: COMMAND_GETKEYS, - COMMAND_GETKEYSANDFLAGS, - commandGetKeysAndFlags: COMMAND_GETKEYSANDFLAGS, - COMMAND_INFO, - commandInfo: COMMAND_INFO, - COMMAND_LIST, - commandList: COMMAND_LIST, - COMMAND, - command: COMMAND, - CONFIG_GET, - configGet: CONFIG_GET, - CONFIG_RESETASTAT, - configResetStat: CONFIG_RESETASTAT, - CONFIG_REWRITE, - configRewrite: CONFIG_REWRITE, - CONFIG_SET, - configSet: CONFIG_SET, - DBSIZE, - dbSize: DBSIZE, - DISCARD, - discard: DISCARD, - ECHO, - echo: ECHO, - FAILOVER, - failover: FAILOVER, - FLUSHALL, - flushAll: FLUSHALL, - FLUSHDB, - flushDb: FLUSHDB, - FUNCTION_DELETE, - functionDelete: FUNCTION_DELETE, - FUNCTION_DUMP, - functionDump: FUNCTION_DUMP, - FUNCTION_FLUSH, - functionFlush: FUNCTION_FLUSH, - FUNCTION_KILL, - functionKill: FUNCTION_KILL, - FUNCTION_LIST_WITHCODE, - functionListWithCode: FUNCTION_LIST_WITHCODE, - FUNCTION_LIST, - functionList: FUNCTION_LIST, - FUNCTION_LOAD, - functionLoad: FUNCTION_LOAD, - FUNCTION_RESTORE, - functionRestore: FUNCTION_RESTORE, - FUNCTION_STATS, - functionStats: FUNCTION_STATS, - HELLO, - hello: HELLO, - INFO, - info: INFO, - KEYS, - keys: KEYS, - LASTSAVE, - lastSave: LASTSAVE, - LATENCY_DOCTOR, - latencyDoctor: LATENCY_DOCTOR, - LATENCY_GRAPH, - latencyGraph: LATENCY_GRAPH, - LOLWUT, - lolwut: LOLWUT, - MEMORY_DOCTOR, - memoryDoctor: MEMORY_DOCTOR, - 'MEMORY_MALLOC-STATS': MEMORY_MALLOC_STATS, - memoryMallocStats: MEMORY_MALLOC_STATS, - MEMORY_PURGE, - memoryPurge: MEMORY_PURGE, - MEMORY_STATS, - memoryStats: MEMORY_STATS, - MEMORY_USAGE, - memoryUsage: MEMORY_USAGE, - MODULE_LIST, - moduleList: MODULE_LIST, - MODULE_LOAD, - moduleLoad: MODULE_LOAD, - MODULE_UNLOAD, - moduleUnload: MODULE_UNLOAD, - MOVE, - move: MOVE, - PING, - ping: PING, - PUBSUB_CHANNELS, - pubSubChannels: PUBSUB_CHANNELS, - PUBSUB_NUMPAT, - pubSubNumPat: PUBSUB_NUMPAT, - PUBSUB_NUMSUB, - pubSubNumSub: PUBSUB_NUMSUB, - PUBSUB_SHARDCHANNELS, - pubSubShardChannels: PUBSUB_SHARDCHANNELS, - RANDOMKEY, - randomKey: RANDOMKEY, - READONLY, - readonly: READONLY, - READWRITE, - readwrite: READWRITE, - REPLICAOF, - replicaOf: REPLICAOF, - 'RESTORE-ASKING': RESTORE_ASKING, - restoreAsking: RESTORE_ASKING, - ROLE, - role: ROLE, - SAVE, - save: SAVE, - SCAN, - scan: SCAN, - SCRIPT_DEBUG, - scriptDebug: SCRIPT_DEBUG, - SCRIPT_EXISTS, - scriptExists: SCRIPT_EXISTS, - SCRIPT_FLUSH, - scriptFlush: SCRIPT_FLUSH, - SCRIPT_KILL, - scriptKill: SCRIPT_KILL, - SCRIPT_LOAD, - scriptLoad: SCRIPT_LOAD, - SHUTDOWN, - shutdown: SHUTDOWN, - SWAPDB, - swapDb: SWAPDB, - TIME, - time: TIME, - UNWATCH, - unwatch: UNWATCH, - WAIT, - wait: WAIT -}; diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index a0824e440ba..8003590e70b 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -1,981 +1,981 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; -import RedisClient, { RedisClientType } from '.'; -import { RedisClientMultiCommandType } from './multi-command'; -import { RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; -import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; -import { defineScript } from '../lua-script'; -import { spy } from 'sinon'; -import { once } from 'events'; -import { ClientKillFilters } from '../commands/CLIENT_KILL'; -import { promisify } from 'util'; - -export const SQUARE_SCRIPT = defineScript({ - SCRIPT: 'return ARGV[1] * ARGV[1];', - NUMBER_OF_KEYS: 0, - transformArguments(number: number): Array { - return [number.toString()]; - } -}); - -export const MATH_FUNCTION = { - name: 'math', - engine: 'LUA', - code: `#!LUA name=math - redis.register_function{ - function_name = "square", - callback = function(keys, args) return args[1] * args[1] end, - flags = { "no-writes" } - }`, - library: { - square: { - NAME: 'square', - IS_READ_ONLY: true, - NUMBER_OF_KEYS: 0, - transformArguments(number: number): Array { - return [number.toString()]; - } - } - } -}; - -export async function loadMathFunction( - client: RedisClientType -): Promise { - await client.functionLoad( - MATH_FUNCTION.code, - { REPLACE: true } - ); -} - -describe('Client', () => { - describe('parseURL', () => { - it('redis://user:secret@localhost:6379/0', () => { - assert.deepEqual( - RedisClient.parseURL('redis://user:secret@localhost:6379/0'), - { - socket: { - host: 'localhost', - port: 6379 - }, - username: 'user', - password: 'secret', - database: 0 - } - ); - }); - - it('rediss://user:secret@localhost:6379/0', () => { - assert.deepEqual( - RedisClient.parseURL('rediss://user:secret@localhost:6379/0'), - { - socket: { - host: 'localhost', - port: 6379, - tls: true - }, - username: 'user', - password: 'secret', - database: 0 - } - ); - }); - - it('Invalid protocol', () => { - assert.throws( - () => RedisClient.parseURL('redi://user:secret@localhost:6379/0'), - TypeError - ); - }); - - it('Invalid pathname', () => { - assert.throws( - () => RedisClient.parseURL('redis://user:secret@localhost:6379/NaN'), - TypeError - ); - }); - - it('redis://localhost', () => { - assert.deepEqual( - RedisClient.parseURL('redis://localhost'), - { - socket: { - host: 'localhost', - } - } - ); - }); - }); - - describe('authentication', () => { - testUtils.testWithClient('Client should be authenticated', async client => { - assert.equal( - await client.ping(), - 'PONG' - ); - }, GLOBAL.SERVERS.PASSWORD); - - testUtils.testWithClient('should execute AUTH before SELECT', async client => { - assert.equal( - (await client.clientInfo()).db, - 2 - ); - }, { - ...GLOBAL.SERVERS.PASSWORD, - clientOptions: { - ...GLOBAL.SERVERS.PASSWORD.clientOptions, - database: 2 - }, - minimumDockerVersion: [6, 2] - }); - }); - - testUtils.testWithClient('should set connection name', async client => { - assert.equal( - await client.clientGetName(), - 'name' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - name: 'name' - } - }); - - describe('legacyMode', () => { - testUtils.testWithClient('client.sendCommand should call the callback', async client => { - assert.equal( - await promisify(client.sendCommand).call(client, 'PING'), - 'PONG' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.sendCommand should work without callback', async client => { - client.sendCommand(['PING']); - await client.v4.ping(); // make sure the first command was replied - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.sendCommand should reply with error', async client => { - await assert.rejects( - promisify(client.sendCommand).call(client, '1', '2') - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.hGetAll should reply with error', async client => { - await assert.rejects( - promisify(client.hGetAll).call(client) - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.v4.sendCommand should return a promise', async client => { - assert.equal( - await client.v4.sendCommand(['PING']), - 'PONG' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.v4.{command} should return a promise', async client => { - assert.equal( - await client.v4.ping(), - 'PONG' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.{command} should accept vardict arguments', async client => { - assert.equal( - await promisify(client.set).call(client, 'a', 'b'), - 'OK' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.{command} should accept arguments array', async client => { - assert.equal( - await promisify(client.set).call(client, ['a', 'b']), - 'OK' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.{command} should accept mix of arrays and arguments', async client => { - assert.equal( - await promisify(client.set).call(client, ['a'], 'b', ['EX', 1]), - 'OK' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.hGetAll should return object', async client => { - await client.v4.hSet('key', 'field', 'value'); +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; +// import RedisClient, { RedisClientType } from '.'; +// import { RedisClientMultiCommandType } from './multi-command'; +// import { RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands'; +// import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; +// import { defineScript } from '../lua-script'; +// import { spy } from 'sinon'; +// import { once } from 'events'; +// import { ClientKillFilters } from '../commands/CLIENT_KILL'; +// import { promisify } from 'util'; + +// export const SQUARE_SCRIPT = defineScript({ +// SCRIPT: 'return ARGV[1] * ARGV[1];', +// NUMBER_OF_KEYS: 0, +// transformArguments(number: number): Array { +// return [number.toString()]; +// } +// }); + +// export const MATH_FUNCTION = { +// name: 'math', +// engine: 'LUA', +// code: `#!LUA name=math +// redis.register_function{ +// function_name = "square", +// callback = function(keys, args) return args[1] * args[1] end, +// flags = { "no-writes" } +// }`, +// library: { +// square: { +// NAME: 'square', +// IS_READ_ONLY: true, +// NUMBER_OF_KEYS: 0, +// transformArguments(number: number): Array { +// return [number.toString()]; +// } +// } +// } +// }; + +// export async function loadMathFunction( +// client: RedisClientType +// ): Promise { +// await client.functionLoad( +// MATH_FUNCTION.code, +// { REPLACE: true } +// ); +// } + +// describe('Client', () => { +// describe('parseURL', () => { +// it('redis://user:secret@localhost:6379/0', () => { +// assert.deepEqual( +// RedisClient.parseURL('redis://user:secret@localhost:6379/0'), +// { +// socket: { +// host: 'localhost', +// port: 6379 +// }, +// username: 'user', +// password: 'secret', +// database: 0 +// } +// ); +// }); + +// it('rediss://user:secret@localhost:6379/0', () => { +// assert.deepEqual( +// RedisClient.parseURL('rediss://user:secret@localhost:6379/0'), +// { +// socket: { +// host: 'localhost', +// port: 6379, +// tls: true +// }, +// username: 'user', +// password: 'secret', +// database: 0 +// } +// ); +// }); + +// it('Invalid protocol', () => { +// assert.throws( +// () => RedisClient.parseURL('redi://user:secret@localhost:6379/0'), +// TypeError +// ); +// }); + +// it('Invalid pathname', () => { +// assert.throws( +// () => RedisClient.parseURL('redis://user:secret@localhost:6379/NaN'), +// TypeError +// ); +// }); + +// it('redis://localhost', () => { +// assert.deepEqual( +// RedisClient.parseURL('redis://localhost'), +// { +// socket: { +// host: 'localhost', +// } +// } +// ); +// }); +// }); + +// describe('authentication', () => { +// testUtils.testWithClient('Client should be authenticated', async client => { +// assert.equal( +// await client.ping(), +// 'PONG' +// ); +// }, GLOBAL.SERVERS.PASSWORD); + +// testUtils.testWithClient('should execute AUTH before SELECT', async client => { +// assert.equal( +// (await client.clientInfo()).db, +// 2 +// ); +// }, { +// ...GLOBAL.SERVERS.PASSWORD, +// clientOptions: { +// ...GLOBAL.SERVERS.PASSWORD.clientOptions, +// database: 2 +// }, +// minimumDockerVersion: [6, 2] +// }); +// }); + +// testUtils.testWithClient('should set connection name', async client => { +// assert.equal( +// await client.clientGetName(), +// 'name' +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// name: 'name' +// } +// }); + +// describe('legacyMode', () => { +// testUtils.testWithClient('client.sendCommand should call the callback', async client => { +// assert.equal( +// await promisify(client.sendCommand).call(client, 'PING'), +// 'PONG' +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.sendCommand should work without callback', async client => { +// client.sendCommand(['PING']); +// await client.v4.ping(); // make sure the first command was replied +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.sendCommand should reply with error', async client => { +// await assert.rejects( +// promisify(client.sendCommand).call(client, '1', '2') +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.hGetAll should reply with error', async client => { +// await assert.rejects( +// promisify(client.hGetAll).call(client) +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.v4.sendCommand should return a promise', async client => { +// assert.equal( +// await client.v4.sendCommand(['PING']), +// 'PONG' +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.v4.{command} should return a promise', async client => { +// assert.equal( +// await client.v4.ping(), +// 'PONG' +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.{command} should accept vardict arguments', async client => { +// assert.equal( +// await promisify(client.set).call(client, 'a', 'b'), +// 'OK' +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.{command} should accept arguments array', async client => { +// assert.equal( +// await promisify(client.set).call(client, ['a', 'b']), +// 'OK' +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.{command} should accept mix of arrays and arguments', async client => { +// assert.equal( +// await promisify(client.set).call(client, ['a'], 'b', ['EX', 1]), +// 'OK' +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.hGetAll should return object', async client => { +// await client.v4.hSet('key', 'field', 'value'); - assert.deepEqual( - await promisify(client.hGetAll).call(client, 'key'), - Object.create(null, { - field: { - value: 'value', - configurable: true, - enumerable: true - } - }) - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - function multiExecAsync< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(multi: RedisClientMultiCommandType): Promise> { - return new Promise((resolve, reject) => { - (multi as any).exec((err: Error | undefined, replies: Array) => { - if (err) return reject(err); - - resolve(replies); - }); - }); - } - - testUtils.testWithClient('client.multi.ping.exec should call the callback', async client => { - assert.deepEqual( - await multiExecAsync( - client.multi().ping() - ), - ['PONG'] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.multi.ping.exec should call the callback', async client => { - client.multi() - .ping() - .exec(); - await client.v4.ping(); // make sure the first command was replied - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.multi.ping.v4.ping.v4.exec should return a promise', async client => { - assert.deepEqual( - await client.multi() - .ping() - .v4.ping() - .v4.exec(), - ['PONG', 'PONG'] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.{script} should return a promise', async client => { - assert.equal( - await client.square(2), - 4 - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true, - scripts: { - square: SQUARE_SCRIPT - } - } - }); - - testUtils.testWithClient('client.multi.{command}.exec should flatten array arguments', async client => { - assert.deepEqual( - await client.multi() - .sAdd('a', ['b', 'c']) - .v4.exec(), - [2] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - - testUtils.testWithClient('client.multi.hGetAll should return object', async client => { - assert.deepEqual( - await multiExecAsync( - client.multi() - .hSet('key', 'field', 'value') - .hGetAll('key') - ), - [ - 1, - Object.create(null, { - field: { - value: 'value', - configurable: true, - enumerable: true - } - }) - ] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - legacyMode: true - } - }); - }); - - describe('events', () => { - testUtils.testWithClient('connect, ready, end', async client => { - await Promise.all([ - once(client, 'connect'), - once(client, 'ready'), - client.connect() - ]); - - await Promise.all([ - once(client, 'end'), - client.disconnect() - ]); - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); - }); - - describe('sendCommand', () => { - testUtils.testWithClient('PING', async client => { - assert.equal(await client.sendCommand(['PING']), 'PONG'); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('returnBuffers', async client => { - assert.deepEqual( - await client.sendCommand(['PING'], { - returnBuffers: true - }), - Buffer.from('PONG') - ); - }, GLOBAL.SERVERS.OPEN); - - describe('AbortController', () => { - before(function () { - if (!global.AbortController) { - this.skip(); - } - }); - - testUtils.testWithClient('success', async client => { - await client.sendCommand(['PING'], { - signal: new AbortController().signal - }); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('AbortError', client => { - const controller = new AbortController(); - controller.abort(); - - return assert.rejects( - client.sendCommand(['PING'], { - signal: controller.signal - }), - AbortError - ); - }, GLOBAL.SERVERS.OPEN); - }); - - testUtils.testWithClient('undefined and null should not break the client', async client => { - await assert.rejects( - client.sendCommand([null as any, undefined as any]), - TypeError - ); - - assert.equal( - await client.ping(), - 'PONG' - ); - }, GLOBAL.SERVERS.OPEN); - }); - - describe('multi', () => { - testUtils.testWithClient('simple', async client => { - assert.deepEqual( - await client.multi() - .ping() - .set('key', 'value') - .get('key') - .exec(), - ['PONG', 'OK', 'value'] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should reject the whole chain on error', client => { - return assert.rejects( - client.multi() - .ping() - .addCommand(['INVALID COMMAND']) - .ping() - .exec() - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should reject the whole chain upon client disconnect', async client => { - await client.disconnect(); - - return assert.rejects( - client.multi() - .ping() - .set('key', 'value') - .get('key') - .exec(), - ClientClosedError - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('with script', async client => { - assert.deepEqual( - await client.multi() - .square(2) - .exec(), - [4] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - scripts: { - square: SQUARE_SCRIPT - } - } - }); - - testUtils.testWithClient('WatchError', async client => { - await client.watch('key'); - - await client.set( - RedisClient.commandOptions({ - isolated: true - }), - 'key', - '1' - ); - - await assert.rejects( - client.multi() - .decr('key') - .exec(), - WatchError - ); - }, GLOBAL.SERVERS.OPEN); - - describe('execAsPipeline', () => { - testUtils.testWithClient('exec(true)', async client => { - assert.deepEqual( - await client.multi() - .ping() - .exec(true), - ['PONG'] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('empty execAsPipeline', async client => { - assert.deepEqual( - await client.multi().execAsPipeline(), - [] - ); - }, GLOBAL.SERVERS.OPEN); - }); - - testUtils.testWithClient('should remember selected db', async client => { - await client.multi() - .select(1) - .exec(); - await killClient(client); - assert.equal( - (await client.clientInfo()).db, - 1 - ); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [6, 2] // CLIENT INFO - }); - }); - - testUtils.testWithClient('scripts', async client => { - assert.equal( - await client.square(2), - 4 - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - scripts: { - square: SQUARE_SCRIPT - } - } - }); - - const module = { - echo: { - transformArguments(message: string): Array { - return ['ECHO', message]; - }, - transformReply(reply: string): string { - return reply; - } - } - }; - - testUtils.testWithClient('modules', async client => { - assert.equal( - await client.module.echo('message'), - 'message' - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - modules: { - module - } - } - }); - - testUtils.testWithClient('functions', async client => { - await loadMathFunction(client); - - assert.equal( - await client.math.square(2), - 4 - ); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [7, 0], - clientOptions: { - functions: { - math: MATH_FUNCTION.library - } - } - }); - - describe('isolationPool', () => { - testUtils.testWithClient('executeIsolated', async client => { - const id = await client.clientId(), - isolatedId = await client.executeIsolated(isolatedClient => isolatedClient.clientId()); - assert.ok(id !== isolatedId); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should be able to use pool even before connect', async client => { - await client.executeIsolated(() => Promise.resolve()); - // make sure to destroy isolation pool - await client.connect(); - await client.disconnect(); - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); - - testUtils.testWithClient('should work after reconnect (#2406)', async client => { - await client.disconnect(); - await client.connect(); - await client.executeIsolated(() => Promise.resolve()); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should throw ClientClosedError after disconnect', async client => { - await client.connect(); - await client.disconnect(); - await assert.rejects( - client.executeIsolated(() => Promise.resolve()), - ClientClosedError - ); - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); - }); - - async function killClient< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >( - client: RedisClientType, - errorClient: RedisClientType = client - ): Promise { - const onceErrorPromise = once(errorClient, 'error'); - await client.sendCommand(['QUIT']); - await Promise.all([ - onceErrorPromise, - assert.rejects(client.ping(), SocketClosedUnexpectedlyError) - ]); - } - - testUtils.testWithClient('should reconnect when socket disconnects', async client => { - await killClient(client); - await assert.doesNotReject(client.ping()); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should remember selected db', async client => { - await client.select(1); - await killClient(client); - assert.equal( - (await client.clientInfo()).db, - 1 - ); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [6, 2] // CLIENT INFO - }); - - testUtils.testWithClient('should propagated errors from "isolated" clients', client => { - client.on('error', () => { - // ignore errors - }); - return client.executeIsolated(isolated => killClient(isolated, client)); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('scanIterator', async client => { - const promises = [], - keys = new Set(); - for (let i = 0; i < 100; i++) { - const key = i.toString(); - keys.add(key); - promises.push(client.set(key, '')); - } - - await Promise.all(promises); - - const results = new Set(); - for await (const key of client.scanIterator()) { - results.add(key); - } - - assert.deepEqual(keys, results); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('hScanIterator', async client => { - const hash: Record = {}; - for (let i = 0; i < 100; i++) { - hash[i.toString()] = i.toString(); - } - - await client.hSet('key', hash); - - const results: Record = {}; - for await (const { field, value } of client.hScanIterator('key')) { - results[field] = value; - } - - assert.deepEqual(hash, results); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('sScanIterator', async client => { - const members = new Set(); - for (let i = 0; i < 100; i++) { - members.add(i.toString()); - } - - await client.sAdd('key', Array.from(members)); - - const results = new Set(); - for await (const key of client.sScanIterator('key')) { - results.add(key); - } - - assert.deepEqual(members, results); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('zScanIterator', async client => { - const members = []; - for (let i = 0; i < 100; i++) { - members.push({ - score: 1, - value: i.toString() - }); - } - - await client.zAdd('key', members); - - const map = new Map(); - for await (const member of client.zScanIterator('key')) { - map.set(member.value, member.score); - } - - type MemberTuple = [string, number]; - - function sort(a: MemberTuple, b: MemberTuple) { - return Number(b[0]) - Number(a[0]); - } - - assert.deepEqual( - [...map.entries()].sort(sort), - members.map(member => [member.value, member.score]).sort(sort) - ); - }, GLOBAL.SERVERS.OPEN); +// assert.deepEqual( +// await promisify(client.hGetAll).call(client, 'key'), +// Object.create(null, { +// field: { +// value: 'value', +// configurable: true, +// enumerable: true +// } +// }) +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// function multiExecAsync< +// M extends RedisModules, +// F extends RedisFunctions, +// S extends RedisScripts +// >(multi: RedisClientMultiCommandType): Promise> { +// return new Promise((resolve, reject) => { +// (multi as any).exec((err: Error | undefined, replies: Array) => { +// if (err) return reject(err); + +// resolve(replies); +// }); +// }); +// } + +// testUtils.testWithClient('client.multi.ping.exec should call the callback', async client => { +// assert.deepEqual( +// await multiExecAsync( +// client.multi().ping() +// ), +// ['PONG'] +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.multi.ping.exec should call the callback', async client => { +// client.multi() +// .ping() +// .exec(); +// await client.v4.ping(); // make sure the first command was replied +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.multi.ping.v4.ping.v4.exec should return a promise', async client => { +// assert.deepEqual( +// await client.multi() +// .ping() +// .v4.ping() +// .v4.exec(), +// ['PONG', 'PONG'] +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.{script} should return a promise', async client => { +// assert.equal( +// await client.square(2), +// 4 +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true, +// scripts: { +// square: SQUARE_SCRIPT +// } +// } +// }); + +// testUtils.testWithClient('client.multi.{command}.exec should flatten array arguments', async client => { +// assert.deepEqual( +// await client.multi() +// .sAdd('a', ['b', 'c']) +// .v4.exec(), +// [2] +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); + +// testUtils.testWithClient('client.multi.hGetAll should return object', async client => { +// assert.deepEqual( +// await multiExecAsync( +// client.multi() +// .hSet('key', 'field', 'value') +// .hGetAll('key') +// ), +// [ +// 1, +// Object.create(null, { +// field: { +// value: 'value', +// configurable: true, +// enumerable: true +// } +// }) +// ] +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// legacyMode: true +// } +// }); +// }); + +// describe('events', () => { +// testUtils.testWithClient('connect, ready, end', async client => { +// await Promise.all([ +// once(client, 'connect'), +// once(client, 'ready'), +// client.connect() +// ]); + +// await Promise.all([ +// once(client, 'end'), +// client.disconnect() +// ]); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// disableClientSetup: true +// }); +// }); + +// describe('sendCommand', () => { +// testUtils.testWithClient('PING', async client => { +// assert.equal(await client.sendCommand(['PING']), 'PONG'); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('returnBuffers', async client => { +// assert.deepEqual( +// await client.sendCommand(['PING'], { +// returnBuffers: true +// }), +// Buffer.from('PONG') +// ); +// }, GLOBAL.SERVERS.OPEN); + +// describe('AbortController', () => { +// before(function () { +// if (!global.AbortController) { +// this.skip(); +// } +// }); + +// testUtils.testWithClient('success', async client => { +// await client.sendCommand(['PING'], { +// signal: new AbortController().signal +// }); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('AbortError', client => { +// const controller = new AbortController(); +// controller.abort(); + +// return assert.rejects( +// client.sendCommand(['PING'], { +// signal: controller.signal +// }), +// AbortError +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); + +// testUtils.testWithClient('undefined and null should not break the client', async client => { +// await assert.rejects( +// client.sendCommand([null as any, undefined as any]), +// TypeError +// ); + +// assert.equal( +// await client.ping(), +// 'PONG' +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); + +// describe('multi', () => { +// testUtils.testWithClient('simple', async client => { +// assert.deepEqual( +// await client.multi() +// .ping() +// .set('key', 'value') +// .get('key') +// .exec(), +// ['PONG', 'OK', 'value'] +// ); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('should reject the whole chain on error', client => { +// return assert.rejects( +// client.multi() +// .ping() +// .addCommand(['INVALID COMMAND']) +// .ping() +// .exec() +// ); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('should reject the whole chain upon client disconnect', async client => { +// await client.disconnect(); + +// return assert.rejects( +// client.multi() +// .ping() +// .set('key', 'value') +// .get('key') +// .exec(), +// ClientClosedError +// ); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('with script', async client => { +// assert.deepEqual( +// await client.multi() +// .square(2) +// .exec(), +// [4] +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// scripts: { +// square: SQUARE_SCRIPT +// } +// } +// }); + +// testUtils.testWithClient('WatchError', async client => { +// await client.watch('key'); + +// await client.set( +// RedisClient.commandOptions({ +// isolated: true +// }), +// 'key', +// '1' +// ); + +// await assert.rejects( +// client.multi() +// .decr('key') +// .exec(), +// WatchError +// ); +// }, GLOBAL.SERVERS.OPEN); + +// describe('execAsPipeline', () => { +// testUtils.testWithClient('exec(true)', async client => { +// assert.deepEqual( +// await client.multi() +// .ping() +// .exec(true), +// ['PONG'] +// ); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('empty execAsPipeline', async client => { +// assert.deepEqual( +// await client.multi().execAsPipeline(), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); + +// testUtils.testWithClient('should remember selected db', async client => { +// await client.multi() +// .select(1) +// .exec(); +// await killClient(client); +// assert.equal( +// (await client.clientInfo()).db, +// 1 +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// minimumDockerVersion: [6, 2] // CLIENT INFO +// }); +// }); + +// testUtils.testWithClient('scripts', async client => { +// assert.equal( +// await client.square(2), +// 4 +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// scripts: { +// square: SQUARE_SCRIPT +// } +// } +// }); + +// const module = { +// echo: { +// transformArguments(message: string): Array { +// return ['ECHO', message]; +// }, +// transformReply(reply: string): string { +// return reply; +// } +// } +// }; + +// testUtils.testWithClient('modules', async client => { +// assert.equal( +// await client.module.echo('message'), +// 'message' +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// modules: { +// module +// } +// } +// }); + +// testUtils.testWithClient('functions', async client => { +// await loadMathFunction(client); + +// assert.equal( +// await client.math.square(2), +// 4 +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// minimumDockerVersion: [7, 0], +// clientOptions: { +// functions: { +// math: MATH_FUNCTION.library +// } +// } +// }); + +// describe('isolationPool', () => { +// testUtils.testWithClient('executeIsolated', async client => { +// const id = await client.clientId(), +// isolatedId = await client.executeIsolated(isolatedClient => isolatedClient.clientId()); +// assert.ok(id !== isolatedId); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('should be able to use pool even before connect', async client => { +// await client.executeIsolated(() => Promise.resolve()); +// // make sure to destroy isolation pool +// await client.connect(); +// await client.disconnect(); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// disableClientSetup: true +// }); + +// testUtils.testWithClient('should work after reconnect (#2406)', async client => { +// await client.disconnect(); +// await client.connect(); +// await client.executeIsolated(() => Promise.resolve()); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('should throw ClientClosedError after disconnect', async client => { +// await client.connect(); +// await client.disconnect(); +// await assert.rejects( +// client.executeIsolated(() => Promise.resolve()), +// ClientClosedError +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// disableClientSetup: true +// }); +// }); + +// async function killClient< +// M extends RedisModules, +// F extends RedisFunctions, +// S extends RedisScripts +// >( +// client: RedisClientType, +// errorClient: RedisClientType = client +// ): Promise { +// const onceErrorPromise = once(errorClient, 'error'); +// await client.sendCommand(['QUIT']); +// await Promise.all([ +// onceErrorPromise, +// assert.rejects(client.ping(), SocketClosedUnexpectedlyError) +// ]); +// } + +// testUtils.testWithClient('should reconnect when socket disconnects', async client => { +// await killClient(client); +// await assert.doesNotReject(client.ping()); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('should remember selected db', async client => { +// await client.select(1); +// await killClient(client); +// assert.equal( +// (await client.clientInfo()).db, +// 1 +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// minimumDockerVersion: [6, 2] // CLIENT INFO +// }); + +// testUtils.testWithClient('should propagated errors from "isolated" clients', client => { +// client.on('error', () => { +// // ignore errors +// }); +// return client.executeIsolated(isolated => killClient(isolated, client)); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('scanIterator', async client => { +// const promises = [], +// keys = new Set(); +// for (let i = 0; i < 100; i++) { +// const key = i.toString(); +// keys.add(key); +// promises.push(client.set(key, '')); +// } + +// await Promise.all(promises); + +// const results = new Set(); +// for await (const key of client.scanIterator()) { +// results.add(key); +// } + +// assert.deepEqual(keys, results); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('hScanIterator', async client => { +// const hash: Record = {}; +// for (let i = 0; i < 100; i++) { +// hash[i.toString()] = i.toString(); +// } + +// await client.hSet('key', hash); + +// const results: Record = {}; +// for await (const { field, value } of client.hScanIterator('key')) { +// results[field] = value; +// } + +// assert.deepEqual(hash, results); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('sScanIterator', async client => { +// const members = new Set(); +// for (let i = 0; i < 100; i++) { +// members.add(i.toString()); +// } + +// await client.sAdd('key', Array.from(members)); + +// const results = new Set(); +// for await (const key of client.sScanIterator('key')) { +// results.add(key); +// } + +// assert.deepEqual(members, results); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('zScanIterator', async client => { +// const members = []; +// for (let i = 0; i < 100; i++) { +// members.push({ +// score: 1, +// value: i.toString() +// }); +// } + +// await client.zAdd('key', members); + +// const map = new Map(); +// for await (const member of client.zScanIterator('key')) { +// map.set(member.value, member.score); +// } + +// type MemberTuple = [string, number]; + +// function sort(a: MemberTuple, b: MemberTuple) { +// return Number(b[0]) - Number(a[0]); +// } + +// assert.deepEqual( +// [...map.entries()].sort(sort), +// members.map(member => [member.value, member.score]).sort(sort) +// ); +// }, GLOBAL.SERVERS.OPEN); - describe('PubSub', () => { - testUtils.testWithClient('should be able to publish and subscribe to messages', async publisher => { - function assertStringListener(message: string, channel: string) { - assert.equal(typeof message, 'string'); - assert.equal(typeof channel, 'string'); - } - - function assertBufferListener(message: Buffer, channel: Buffer) { - assert.ok(Buffer.isBuffer(message)); - assert.ok(Buffer.isBuffer(channel)); - } - - const subscriber = publisher.duplicate(); - - await subscriber.connect(); - - try { - const channelListener1 = spy(assertBufferListener), - channelListener2 = spy(assertStringListener), - patternListener = spy(assertStringListener); - - await Promise.all([ - subscriber.subscribe('channel', channelListener1, true), - subscriber.subscribe('channel', channelListener2), - subscriber.pSubscribe('channel*', patternListener) - ]); - await Promise.all([ - waitTillBeenCalled(channelListener1), - waitTillBeenCalled(channelListener2), - waitTillBeenCalled(patternListener), - publisher.publish(Buffer.from('channel'), Buffer.from('message')) - ]); - - assert.ok(channelListener1.calledOnceWithExactly(Buffer.from('message'), Buffer.from('channel'))); - assert.ok(channelListener2.calledOnceWithExactly('message', 'channel')); - assert.ok(patternListener.calledOnceWithExactly('message', 'channel')); - - await subscriber.unsubscribe('channel', channelListener1, true); - await Promise.all([ - waitTillBeenCalled(channelListener2), - waitTillBeenCalled(patternListener), - publisher.publish('channel', 'message') - ]); - assert.ok(channelListener1.calledOnce); - assert.ok(channelListener2.calledTwice); - assert.ok(channelListener2.secondCall.calledWithExactly('message', 'channel')); - assert.ok(patternListener.calledTwice); - assert.ok(patternListener.secondCall.calledWithExactly('message', 'channel')); - await subscriber.unsubscribe('channel'); - await Promise.all([ - waitTillBeenCalled(patternListener), - publisher.publish('channel', 'message') - ]); - assert.ok(channelListener1.calledOnce); - assert.ok(channelListener2.calledTwice); - assert.ok(patternListener.calledThrice); - assert.ok(patternListener.thirdCall.calledWithExactly('message', 'channel')); - await subscriber.pUnsubscribe(); - await publisher.publish('channel', 'message'); - assert.ok(channelListener1.calledOnce); - assert.ok(channelListener2.calledTwice); - assert.ok(patternListener.calledThrice); - // should be able to send commands when unsubsribed from all channels (see #1652) - await assert.doesNotReject(subscriber.ping()); - } finally { - await subscriber.disconnect(); - } - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should resubscribe', async publisher => { - const subscriber = publisher.duplicate(); - - await subscriber.connect(); - - try { - const channelListener = spy(); - await subscriber.subscribe('channel', channelListener); - - const patternListener = spy(); - await subscriber.pSubscribe('channe*', patternListener); - - await Promise.all([ - once(subscriber, 'error'), - publisher.clientKill({ - filter: ClientKillFilters.SKIP_ME, - skipMe: true - }) - ]); - - await once(subscriber, 'ready'); - - await Promise.all([ - waitTillBeenCalled(channelListener), - waitTillBeenCalled(patternListener), - publisher.publish('channel', 'message') - ]); - } finally { - await subscriber.disconnect(); - } - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should not fail when message arrives right after subscribe', async publisher => { - const subscriber = publisher.duplicate(); - - await subscriber.connect(); - - try { - await assert.doesNotReject(Promise.all([ - subscriber.subscribe('channel', () => { - // noop - }), - publisher.publish('channel', 'message') - ])); - } finally { - await subscriber.disconnect(); - } - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should be able to quit in PubSub mode', async client => { - await client.subscribe('channel', () => { - // noop - }); - - await assert.doesNotReject(client.quit()); - - assert.equal(client.isOpen, false); - }, GLOBAL.SERVERS.OPEN); - }); - - testUtils.testWithClient('ConnectionTimeoutError', async client => { - const promise = assert.rejects(client.connect(), ConnectionTimeoutError), - 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 - } - - await promise; - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - socket: { - connectTimeout: 1 - } - }, - disableClientSetup: true - }); - - testUtils.testWithClient('client.quit', async client => { - await client.connect(); - - const pingPromise = client.ping(), - quitPromise = client.quit(); - assert.equal(client.isOpen, false); - - const [ping, quit] = await Promise.all([ - pingPromise, - quitPromise, - assert.rejects(client.ping(), ClientClosedError) - ]); - - assert.equal(ping, 'PONG'); - assert.equal(quit, 'OK'); - }, { - ...GLOBAL.SERVERS.OPEN, - disableClientSetup: true - }); - - testUtils.testWithClient('client.disconnect', async client => { - const pingPromise = client.ping(), - disconnectPromise = client.disconnect(); - assert.equal(client.isOpen, false); - await Promise.all([ - assert.rejects(pingPromise, DisconnectsClientError), - assert.doesNotReject(disconnectPromise), - assert.rejects(client.ping(), ClientClosedError) - ]); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should be able to connect after disconnect (see #1801)', async client => { - await client.disconnect(); - await client.connect(); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('should be able to use ref and unref', client => { - client.unref(); - client.ref(); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('pingInterval', async client => { - assert.deepEqual( - await once(client, 'ping-interval'), - ['PONG'] - ); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - pingInterval: 1 - } - }); - - testUtils.testWithClient('should reject commands in connect phase when `disableOfflineQueue`', async client => { - const connectPromise = client.connect(); - await assert.rejects( - client.ping(), - ClientOfflineError - ); - await connectPromise; - await client.disconnect(); - }, { - ...GLOBAL.SERVERS.OPEN, - clientOptions: { - disableOfflineQueue: true - }, - disableClientSetup: true - }); -}); +// describe('PubSub', () => { +// testUtils.testWithClient('should be able to publish and subscribe to messages', async publisher => { +// function assertStringListener(message: string, channel: string) { +// assert.equal(typeof message, 'string'); +// assert.equal(typeof channel, 'string'); +// } + +// function assertBufferListener(message: Buffer, channel: Buffer) { +// assert.ok(Buffer.isBuffer(message)); +// assert.ok(Buffer.isBuffer(channel)); +// } + +// const subscriber = publisher.duplicate(); + +// await subscriber.connect(); + +// try { +// const channelListener1 = spy(assertBufferListener), +// channelListener2 = spy(assertStringListener), +// patternListener = spy(assertStringListener); + +// await Promise.all([ +// subscriber.subscribe('channel', channelListener1, true), +// subscriber.subscribe('channel', channelListener2), +// subscriber.pSubscribe('channel*', patternListener) +// ]); +// await Promise.all([ +// waitTillBeenCalled(channelListener1), +// waitTillBeenCalled(channelListener2), +// waitTillBeenCalled(patternListener), +// publisher.publish(Buffer.from('channel'), Buffer.from('message')) +// ]); + +// assert.ok(channelListener1.calledOnceWithExactly(Buffer.from('message'), Buffer.from('channel'))); +// assert.ok(channelListener2.calledOnceWithExactly('message', 'channel')); +// assert.ok(patternListener.calledOnceWithExactly('message', 'channel')); + +// await subscriber.unsubscribe('channel', channelListener1, true); +// await Promise.all([ +// waitTillBeenCalled(channelListener2), +// waitTillBeenCalled(patternListener), +// publisher.publish('channel', 'message') +// ]); +// assert.ok(channelListener1.calledOnce); +// assert.ok(channelListener2.calledTwice); +// assert.ok(channelListener2.secondCall.calledWithExactly('message', 'channel')); +// assert.ok(patternListener.calledTwice); +// assert.ok(patternListener.secondCall.calledWithExactly('message', 'channel')); +// await subscriber.unsubscribe('channel'); +// await Promise.all([ +// waitTillBeenCalled(patternListener), +// publisher.publish('channel', 'message') +// ]); +// assert.ok(channelListener1.calledOnce); +// assert.ok(channelListener2.calledTwice); +// assert.ok(patternListener.calledThrice); +// assert.ok(patternListener.thirdCall.calledWithExactly('message', 'channel')); +// await subscriber.pUnsubscribe(); +// await publisher.publish('channel', 'message'); +// assert.ok(channelListener1.calledOnce); +// assert.ok(channelListener2.calledTwice); +// assert.ok(patternListener.calledThrice); +// // should be able to send commands when unsubsribed from all channels (see #1652) +// await assert.doesNotReject(subscriber.ping()); +// } finally { +// await subscriber.disconnect(); +// } +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('should resubscribe', async publisher => { +// const subscriber = publisher.duplicate(); + +// await subscriber.connect(); + +// try { +// const channelListener = spy(); +// await subscriber.subscribe('channel', channelListener); + +// const patternListener = spy(); +// await subscriber.pSubscribe('channe*', patternListener); + +// await Promise.all([ +// once(subscriber, 'error'), +// publisher.clientKill({ +// filter: ClientKillFilters.SKIP_ME, +// skipMe: true +// }) +// ]); + +// await once(subscriber, 'ready'); + +// await Promise.all([ +// waitTillBeenCalled(channelListener), +// waitTillBeenCalled(patternListener), +// publisher.publish('channel', 'message') +// ]); +// } finally { +// await subscriber.disconnect(); +// } +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('should not fail when message arrives right after subscribe', async publisher => { +// const subscriber = publisher.duplicate(); + +// await subscriber.connect(); + +// try { +// await assert.doesNotReject(Promise.all([ +// subscriber.subscribe('channel', () => { +// // noop +// }), +// publisher.publish('channel', 'message') +// ])); +// } finally { +// await subscriber.disconnect(); +// } +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('should be able to quit in PubSub mode', async client => { +// await client.subscribe('channel', () => { +// // noop +// }); + +// await assert.doesNotReject(client.quit()); + +// assert.equal(client.isOpen, false); +// }, GLOBAL.SERVERS.OPEN); +// }); + +// testUtils.testWithClient('ConnectionTimeoutError', async client => { +// const promise = assert.rejects(client.connect(), ConnectionTimeoutError), +// 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 +// } + +// await promise; +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// socket: { +// connectTimeout: 1 +// } +// }, +// disableClientSetup: true +// }); + +// testUtils.testWithClient('client.quit', async client => { +// await client.connect(); + +// const pingPromise = client.ping(), +// quitPromise = client.quit(); +// assert.equal(client.isOpen, false); + +// const [ping, quit] = await Promise.all([ +// pingPromise, +// quitPromise, +// assert.rejects(client.ping(), ClientClosedError) +// ]); + +// assert.equal(ping, 'PONG'); +// assert.equal(quit, 'OK'); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// disableClientSetup: true +// }); + +// testUtils.testWithClient('client.disconnect', async client => { +// const pingPromise = client.ping(), +// disconnectPromise = client.disconnect(); +// assert.equal(client.isOpen, false); +// await Promise.all([ +// assert.rejects(pingPromise, DisconnectsClientError), +// assert.doesNotReject(disconnectPromise), +// assert.rejects(client.ping(), ClientClosedError) +// ]); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('should be able to connect after disconnect (see #1801)', async client => { +// await client.disconnect(); +// await client.connect(); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('should be able to use ref and unref', client => { +// client.unref(); +// client.ref(); +// }, GLOBAL.SERVERS.OPEN); + +// testUtils.testWithClient('pingInterval', async client => { +// assert.deepEqual( +// await once(client, 'ping-interval'), +// ['PONG'] +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// pingInterval: 1 +// } +// }); + +// testUtils.testWithClient('should reject commands in connect phase when `disableOfflineQueue`', async client => { +// const connectPromise = client.connect(); +// await assert.rejects( +// client.ping(), +// ClientOfflineError +// ); +// await connectPromise; +// await client.disconnect(); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// clientOptions: { +// disableOfflineQueue: true +// }, +// disableClientSetup: true +// }); +// }); diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 5b9badf3f37..493d32de9d5 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,832 +1,906 @@ -import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands } from '../commands'; +import COMMANDS from '../commands'; import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { QueueCommandOptions } from './commands-queue'; -import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; -import { RedisMultiQueuedCommand } from '../multi-command'; import { EventEmitter } from 'events'; -import { CommandOptions, commandOptions, isCommandOptions } from '../command-options'; -import { ScanOptions, ZMember } from '../commands/generic-transformers'; -import { ScanCommandOptions } from '../commands/SCAN'; -import { HScanTuple } from '../commands/HSCAN'; -import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; -import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; +import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; +import { Command, CommandArguments, CommandSignature, Flags, CommanderConfig, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument } from '../RESP/types'; +import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; +import { RedisMultiQueuedCommand } from '../multi-command'; +import HELLO, { HelloOptions } from '../commands/HELLO'; +import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; +import { ReplyWithFlags, CommandReply } from '../RESP/types'; +import SCAN, { ScanOptions, ScanCommonOptions } from '../commands/SCAN'; export interface RedisClientOptions< - M extends RedisModules = RedisModules, - F extends RedisFunctions = RedisFunctions, - S extends RedisScripts = RedisScripts -> extends RedisExtensions { - /** - * `redis[s]://[[username][:password]@][host][:port][/db-number]` - * See [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details - */ - url?: string; - /** - * Socket connection properties - */ - socket?: RedisSocketOptions; - /** - * ACL username ([see ACL guide](https://redis.io/topics/acl)) - */ - username?: string; - /** - * ACL password or the old "--requirepass" password - */ - password?: string; - /** - * Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) - */ - name?: string; - /** - * Redis database number (see [`SELECT`](https://redis.io/commands/select) command) - */ - database?: number; - /** - * Maximum length of the client's internal command queue - */ - commandsQueueMaxLength?: number; - /** - * When `true`, commands are rejected when the client is reconnecting. - * When `false`, commands are queued for execution after reconnection. - */ - disableOfflineQueue?: boolean; - /** - * Connect in [`READONLY`](https://redis.io/commands/readonly) mode - */ - readonly?: boolean; - legacyMode?: boolean; - isolationPoolOptions?: PoolOptions; - /** - * Send `PING` command at interval (in ms). - * Useful with Redis deployments that do not use TCP Keep-Alive. - */ - pingInterval?: number; + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts, + RESP extends RespVersions = RespVersions +> extends CommanderConfig { + /** + * `redis[s]://[[username][:password]@][host][:port][/db-number]` + * See [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details + */ + url?: string; + /** + * Socket connection properties + */ + socket?: RedisSocketOptions; + /** + * ACL username ([see ACL guide](https://redis.io/topics/acl)) + */ + username?: string; + /** + * ACL password or the old "--requirepass" password + */ + password?: string; + /** + * Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) + */ + name?: string; + /** + * Redis database number (see [`SELECT`](https://redis.io/commands/select) command) + */ + database?: number; + /** + * Maximum length of the client's internal command queue + */ + commandsQueueMaxLength?: number; + /** + * When `true`, commands are rejected when the client is reconnecting. + * When `false`, commands are queued for execution after reconnection. + */ + disableOfflineQueue?: boolean; + /** + * Connect in [`READONLY`](https://redis.io/commands/readonly) mode + */ + readonly?: boolean; + /** + * TODO + */ + legacyMode?: boolean; + /** + * TODO + */ + isolationPoolOptions?: PoolOptions; + /** + * Send `PING` command at interval (in ms). + * Useful with Redis deployments that do not use TCP Keep-Alive. + */ + pingInterval?: number; } -type WithCommands = { - [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; +type WithCommands< + RESP extends RespVersions, + FLAGS extends Flags +> = { + [P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], RESP, FLAGS>; }; -export type WithModules = { - [P in keyof M as ExcludeMappedString

]: { - [C in keyof M[P] as ExcludeMappedString]: RedisCommandSignature; - }; +type WithModules< + M extends RedisModules, + RESP extends RespVersions, + FLAGS extends Flags +> = { + [P in keyof M]: { + [C in keyof M[P]]: CommandSignature; + }; }; -export type WithFunctions = { - [P in keyof F as ExcludeMappedString

]: { - [FF in keyof F[P] as ExcludeMappedString]: RedisCommandSignature; - }; +type WithFunctions< + F extends RedisFunctions, + RESP extends RespVersions, + FLAGS extends Flags +> = { + [L in keyof F]: { + [C in keyof F[L]]: CommandSignature; + }; }; -export type WithScripts = { - [P in keyof S as ExcludeMappedString

]: RedisCommandSignature; +type WithScripts< + S extends RedisScripts, + RESP extends RespVersions, + FLAGS extends Flags +> = { + [P in keyof S]: CommandSignature; }; export type RedisClientType< - M extends RedisModules = Record, - F extends RedisFunctions = Record, - S extends RedisScripts = Record -> = RedisClient & WithCommands & WithModules & WithFunctions & WithScripts; - -export type InstantiableRedisClient< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = new (options?: RedisClientOptions) => RedisClientType; + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + FLAGS extends Flags = {} +> = ( + RedisClient & + WithCommands & + WithModules & + WithFunctions & + WithScripts +); export interface ClientCommandOptions extends QueueCommandOptions { - isolated?: boolean; + isolated?: boolean; } -type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => void; +// type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => void; + +type ProxyClient = RedisClient<{}, {}, {}, RespVersions, Flags> & { commandOptions?: ClientCommandOptions }; + +type NamespaceProxyClient = { self: ProxyClient }; + +interface ScanIteratorOptions { + cursor?: number; +} export default class RedisClient< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + FLAGS extends Flags > extends EventEmitter { - static commandOptions(options: T): CommandOptions { - return commandOptions(options); - } - - commandOptions = RedisClient.commandOptions; - - static extend< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(extensions?: RedisExtensions): InstantiableRedisClient { - const Client = attachExtensions({ - BaseClass: RedisClient, - modulesExecutor: RedisClient.prototype.commandsExecutor, - modules: extensions?.modules, - functionsExecutor: RedisClient.prototype.functionsExecuter, - functions: extensions?.functions, - scriptsExecutor: RedisClient.prototype.scriptsExecuter, - scripts: extensions?.scripts + private static _createCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: ProxyClient) { + const args = command.transformArguments.apply(undefined, arguments as any), + reply = await this._sendCommand(args, this.commandOptions); + return transformReply ? + transformReply(reply, args.preserve) : + reply; + }; + } + + private static _createModuleCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: NamespaceProxyClient) { + const args = command.transformArguments.apply(undefined, arguments as any), + reply = await this.self._sendCommand(args, this.self.commandOptions); + return transformReply ? + transformReply(reply, args.preserve) : + reply; + }; + } + + private static _createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { + const prefix = functionArgumentsPrefix(name, fn), + transformReply = getTransformReply(fn, resp); + return async function (this: NamespaceProxyClient) { + const fnArgs = fn.transformArguments.apply(undefined, arguments as any), + reply = await this.self._sendCommand( + prefix.concat(fnArgs), + this.self.commandOptions + ); + return transformReply ? + transformReply(reply, fnArgs.preserve) : + reply; + }; + } + + private static _createScriptCommand(script: RedisScript, resp: RespVersions) { + const prefix = scriptArgumentsPrefix(script), + transformReply = getTransformReply(script, resp); + return async function (this: ProxyClient) { + const scriptArgs = script.transformArguments.apply(undefined, arguments as any), + args = prefix.concat(scriptArgs), + reply = await this._sendCommand(args, this.commandOptions).catch((err: unknown) => { + if (!(err as Error)?.message?.startsWith?.('NOSCRIPT')) throw err; + + args[0] = 'EVAL'; + args[1] = script.SCRIPT; + return this._sendCommand(args, this.commandOptions); }); - - if (Client !== RedisClient) { - Client.prototype.Multi = RedisClientMultiCommand.extend(extensions); + return transformReply ? + transformReply(reply, scriptArgs.preserve) : + reply; + }; + } + + static factory< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2 + >(config?: CommanderConfig) { + const Client = attachConfig({ + BaseClass: RedisClient, + commands: COMMANDS, + createCommand: RedisClient._createCommand, + createFunctionCommand: RedisClient._createFunctionCommand, + createModuleCommand: RedisClient._createModuleCommand, + createScriptCommand: RedisClient._createScriptCommand, + config + }); + + Client.prototype.Multi = RedisClientMultiCommand.extend(config); + + return (options?: Omit>) => { + // returning a proxy of the client to prevent the namespaces.self to leak between proxies + // namespaces will be bootstraped on first access per proxy + return Object.create(new Client(options)) as RedisClientType; + }; + } + + static create< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2 + >(options?: RedisClientOptions) { + return RedisClient.factory(options)(options); + } + + static parseURL(url: string): RedisClientOptions { + // https://www.iana.org/assignments/uri-schemes/prov/redis + const { hostname, port, protocol, username, password, pathname } = new URL(url), + parsed: RedisClientOptions = { + socket: { + host: hostname } + }; - return Client; + if (protocol === 'rediss:') { + (parsed.socket as RedisTlsSocketOptions).tls = true; + } else if (protocol !== 'redis:') { + throw new TypeError('Invalid protocol'); } - static create< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(options?: RedisClientOptions): RedisClientType { - return new (RedisClient.extend(options))(options); + if (port) { + (parsed.socket as TcpSocketConnectOpts).port = Number(port); } - static parseURL(url: string): RedisClientOptions { - // https://www.iana.org/assignments/uri-schemes/prov/redis - const { hostname, port, protocol, username, password, pathname } = new URL(url), - parsed: RedisClientOptions = { - socket: { - host: hostname - } - }; - - if (protocol === 'rediss:') { - (parsed.socket as RedisTlsSocketOptions).tls = true; - } else if (protocol !== 'redis:') { - throw new TypeError('Invalid protocol'); - } - - if (port) { - (parsed.socket as TcpSocketConnectOpts).port = Number(port); - } - - if (username) { - parsed.username = decodeURIComponent(username); - } - - if (password) { - parsed.password = decodeURIComponent(password); - } - - if (pathname.length > 1) { - const database = Number(pathname.substring(1)); - if (isNaN(database)) { - throw new TypeError('Invalid pathname'); - } - - parsed.database = database; - } - - return parsed; + if (username) { + parsed.username = decodeURIComponent(username); } - readonly #options?: RedisClientOptions; - readonly #socket: RedisSocket; - readonly #queue: RedisCommandsQueue; - #isolationPool?: Pool>; - readonly #v4: Record = {}; - #selectedDB = 0; - - get options(): RedisClientOptions | undefined { - return this.#options; + if (password) { + parsed.password = decodeURIComponent(password); } - get isOpen(): boolean { - return this.#socket.isOpen; - } + if (pathname.length > 1) { + const database = Number(pathname.substring(1)); + if (isNaN(database)) { + throw new TypeError('Invalid pathname'); + } - get isReady(): boolean { - return this.#socket.isReady; + parsed.database = database; } - get isPubSubActive() { - return this.#queue.isPubSubActive; - } + return parsed; + } - get v4(): Record { - if (!this.#options?.legacyMode) { - throw new Error('the client is not in "legacy mode"'); - } + self = this; - return this.#v4; - } - - constructor(options?: RedisClientOptions) { - super(); - this.#options = this.#initiateOptions(options); - this.#queue = this.#initiateQueue(); - this.#socket = this.#initiateSocket(); - // should be initiated in connect, not here - // TODO: consider breaking in v5 - this.#isolationPool = this.#initiateIsolationPool(); - this.#legacyMode(); - } - - #initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { - if (options?.url) { - const parsed = RedisClient.parseURL(options.url); - if (options.socket) { - parsed.socket = Object.assign(options.socket, parsed.socket); - } - - Object.assign(options, parsed); - } - - if (options?.database) { - this.#selectedDB = options.database; - } - - return options; - } - - #initiateQueue(): RedisCommandsQueue { - return new RedisCommandsQueue( - this.#options?.commandsQueueMaxLength, - (channel, listeners) => this.emit('sharded-channel-moved', channel, listeners) - ); - } - - #initiateSocket(): RedisSocket { - const socketInitiator = async (): Promise => { - const promises = []; - - if (this.#selectedDB !== 0) { - promises.push( - this.#queue.addCommand( - ['SELECT', this.#selectedDB.toString()], - { asap: true } - ) - ); - } - - if (this.#options?.readonly) { - promises.push( - this.#queue.addCommand( - COMMANDS.READONLY.transformArguments(), - { asap: true } - ) - ); - } - - if (this.#options?.name) { - promises.push( - this.#queue.addCommand( - COMMANDS.CLIENT_SETNAME.transformArguments(this.#options.name), - { asap: true } - ) - ); - } - - if (this.#options?.username || this.#options?.password) { - promises.push( - this.#queue.addCommand( - COMMANDS.AUTH.transformArguments({ - username: this.#options.username, - password: this.#options.password ?? '' - }), - { asap: true } - ) - ); - } - - const resubscribePromise = this.#queue.resubscribe(); - if (resubscribePromise) { - promises.push(resubscribePromise); - } - - if (promises.length) { - this.#tick(true); - await Promise.all(promises); - } - }; - - return new RedisSocket(socketInitiator, this.#options?.socket) - .on('data', chunk => this.#queue.onReplyChunk(chunk)) - .on('error', err => { - this.emit('error', err); - if (this.#socket.isOpen && !this.#options?.disableOfflineQueue) { - this.#queue.flushWaitingForReply(err); - } else { - this.#queue.flushAll(err); - } - }) - .on('connect', () => { - this.emit('connect'); - }) - .on('ready', () => { - this.emit('ready'); - this.#setPingTimer(); - this.#tick(); - }) - .on('reconnecting', () => this.emit('reconnecting')) - .on('drain', () => this.#tick()) - .on('end', () => this.emit('end')); - } - - #initiateIsolationPool() { - return createPool({ - create: async () => { - const duplicate = this.duplicate({ - isolationPoolOptions: undefined - }).on('error', err => this.emit('error', err)); - await duplicate.connect(); - return duplicate; - }, - destroy: client => client.disconnect() - }, this.#options?.isolationPoolOptions); - } - - #legacyMode(): void { - if (!this.#options?.legacyMode) return; - - (this as any).#v4.sendCommand = this.#sendCommand.bind(this); - (this as any).sendCommand = (...args: Array): void => { - const result = this.#legacySendCommand(...args); - if (result) { - result.promise - .then(reply => result.callback(null, reply)) - .catch(err => result.callback(err)); - } - }; - - for (const [ name, command ] of Object.entries(COMMANDS as RedisCommands)) { - this.#defineLegacyCommand(name, command); - (this as any)[name.toLowerCase()] ??= (this as any)[name]; - } + private readonly _options?: RedisClientOptions; + private readonly _socket: RedisSocket; + private readonly _queue: RedisCommandsQueue; + private _isolationPool?: Pool>; + // readonly #v4: Record = {}; + private _selectedDB = 0; - // hard coded commands - this.#defineLegacyCommand('SELECT'); - this.#defineLegacyCommand('select'); - this.#defineLegacyCommand('SUBSCRIBE'); - this.#defineLegacyCommand('subscribe'); - this.#defineLegacyCommand('PSUBSCRIBE'); - this.#defineLegacyCommand('pSubscribe'); - this.#defineLegacyCommand('UNSUBSCRIBE'); - this.#defineLegacyCommand('unsubscribe'); - this.#defineLegacyCommand('PUNSUBSCRIBE'); - this.#defineLegacyCommand('pUnsubscribe'); - this.#defineLegacyCommand('QUIT'); - this.#defineLegacyCommand('quit'); - } + get options(): RedisClientOptions | undefined { + return this._options; + } - #legacySendCommand(...args: Array) { - const callback = typeof args[args.length - 1] === 'function' ? - args.pop() as ClientLegacyCallback : - undefined; - - const promise = this.#sendCommand(transformLegacyCommandArguments(args)); - if (callback) return { - promise, - callback - }; - promise.catch(err => this.emit('error', err)); - } + get isOpen(): boolean { + return this._socket.isOpen; + } - #defineLegacyCommand(name: string, command?: RedisCommand): void { - this.#v4[name] = (this as any)[name].bind(this); - (this as any)[name] = command && command.TRANSFORM_LEGACY_REPLY && command.transformReply ? - (...args: Array) => { - const result = this.#legacySendCommand(name, ...args); - if (result) { - result.promise - .then(reply => result.callback(null, command.transformReply!(reply))) - .catch(err => result.callback(err)); - } - } : - (...args: Array) => (this as any).sendCommand(name, ...args); - } + get isReady(): boolean { + return this._socket.isReady; + } - #pingTimer?: NodeJS.Timer; + get isPubSubActive() { + return this._queue.isPubSubActive; + } - #setPingTimer(): void { - if (!this.#options?.pingInterval || !this.#socket.isReady) return; - clearTimeout(this.#pingTimer); + // get v4(): Record { + // if (!this.client.#options?.legacyMode) { + // throw new Error('the client is not in "legacy mode"'); + // } - this.#pingTimer = setTimeout(() => { - if (!this.#socket.isReady) return; + // return this.client.#v4; + // } - // using #sendCommand to support legacy mode - this.#sendCommand(['PING']) - .then(reply => this.emit('ping-interval', reply)) - .catch(err => this.emit('error', err)) - .finally(() => this.#setPingTimer()); - }, this.#options.pingInterval); - } + constructor(options?: RedisClientOptions) { + super(); + this._options = this._initiateOptions(options); + this._queue = this._initiateQueue(); + this._socket = this._initiateSocket(); + // this.#legacyMode(); + } - duplicate(overrides?: Partial>): RedisClientType { - return new (Object.getPrototypeOf(this).constructor)({ - ...this.#options, - ...overrides - }); - } - - connect(): Promise { - // see comment in constructor - this.#isolationPool ??= this.#initiateIsolationPool(); - return this.#socket.connect(); - } + private _initiateOptions(options?: RedisClientOptions): RedisClientOptions | undefined { + if (options?.url) { + const parsed = RedisClient.parseURL(options.url); + if (options.socket) { + parsed.socket = Object.assign(options.socket, parsed.socket); + } - async commandsExecutor( - command: C, - args: Array - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(command, args); - return transformCommandReply( - command, - await this.#sendCommand(redisArgs, options), - redisArgs.preserve - ); + Object.assign(options, parsed); } - sendCommand( - args: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - return this.#sendCommand(args, options); + if (options?.database) { + this._selectedDB = options.database; } - // using `#sendCommand` cause `sendCommand` is overwritten in legacy mode - #sendCommand( - args: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - if (!this.#socket.isOpen) { - return Promise.reject(new ClientClosedError()); - } else if (options?.isolated) { - return this.executeIsolated(isolatedClient => - isolatedClient.sendCommand(args, { - ...options, - isolated: false - }) - ); - } else if (!this.#socket.isReady && this.#options?.disableOfflineQueue) { - return Promise.reject(new ClientOfflineError()); - } + return options; + } - const promise = this.#queue.addCommand(args, options); - this.#tick(); - return promise; - } + private _initiateQueue(): RedisCommandsQueue { + return new RedisCommandsQueue( + this._options?.RESP, + this._options?.commandsQueueMaxLength, + (channel, listeners) => this.emit('sharded-channel-moved', channel, listeners) + ); + } - async functionsExecuter( - fn: F, - args: Array, - name: string - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(fn, args); - return transformCommandReply( - fn, - await this.executeFunction(name, fn, redisArgs, options), - redisArgs.preserve - ); - } + private _initiateSocket(): RedisSocket { + const socketInitiator = async (): Promise => { + const promises = []; - executeFunction( - name: string, - fn: RedisFunction, - args: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - return this.#sendCommand( - fCallArguments(name, fn, args), - options + if (this._selectedDB !== 0) { + promises.push( + this._queue.addCommand( + ['SELECT', this._selectedDB.toString()], + { asap: true } + ) ); - } - - async scriptsExecuter( - script: S, - args: Array - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(script, args); - return transformCommandReply( - script, - await this.executeScript(script, redisArgs, options), - redisArgs.preserve - ); - } - - async executeScript( - script: RedisScript, - args: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - const redisArgs: RedisCommandArguments = ['EVALSHA', script.SHA1]; - - if (script.NUMBER_OF_KEYS !== undefined) { - redisArgs.push(script.NUMBER_OF_KEYS.toString()); - } - - redisArgs.push(...args); - - try { - return await this.#sendCommand(redisArgs, options); - } catch (err: any) { - if (!err?.message?.startsWith?.('NOSCRIPT')) { - throw err; - } - - redisArgs[0] = 'EVAL'; - redisArgs[1] = script.SCRIPT; - return this.#sendCommand(redisArgs, options); + } + + if (this._options?.readonly) { + // promises.push( + // this.#queue.addCommand( + // COMMANDS.READONLY.transformArguments(), + // { asap: true } + // ) + // ); + } + + if (this._options?.RESP) { + const hello: HelloOptions = {}; + + if (this._options.password) { + hello.AUTH = { + username: this._options.username ?? 'default', + password: this._options.password + }; } - } - async SELECT(db: number): Promise; - async SELECT(options: CommandOptions, db: number): Promise; - async SELECT(options?: any, db?: any): Promise { - if (!isCommandOptions(options)) { - db = options; - options = null; + if (this._options.name) { + hello.SETNAME = this._options.name; } - await this.#sendCommand(['SELECT', db.toString()], options); - this.#selectedDB = db; - } - - select = this.SELECT; - - #pubSubCommand(promise: Promise | undefined) { - if (promise === undefined) return Promise.resolve(); - - this.#tick(); - return promise; - } - - SUBSCRIBE( - channels: string | Array, - listener: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.subscribe( - PubSubType.CHANNELS, - channels, - listener, - bufferMode - ) - ); - } - - subscribe = this.SUBSCRIBE; - - - UNSUBSCRIBE( - channels?: string | Array, - listener?: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.unsubscribe( - PubSubType.CHANNELS, - channels, - listener, - bufferMode - ) - ); - } - - unsubscribe = this.UNSUBSCRIBE; - - PSUBSCRIBE( - patterns: string | Array, - listener: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.subscribe( - PubSubType.PATTERNS, - patterns, - listener, - bufferMode - ) - ); - } - - pSubscribe = this.PSUBSCRIBE; - - PUNSUBSCRIBE( - patterns?: string | Array, - listener?: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.unsubscribe( - PubSubType.PATTERNS, - patterns, - listener, - bufferMode - ) - ); - } - - pUnsubscribe = this.PUNSUBSCRIBE; - - SSUBSCRIBE( - channels: string | Array, - listener: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.subscribe( - PubSubType.SHARDED, - channels, - listener, - bufferMode - ) - ); - } - - sSubscribe = this.SSUBSCRIBE; - - SUNSUBSCRIBE( - channels?: string | Array, - listener?: PubSubListener, - bufferMode?: T - ): Promise { - return this.#pubSubCommand( - this.#queue.unsubscribe( - PubSubType.SHARDED, - channels, - listener, - bufferMode - ) - ); - } - - sUnsubscribe = this.SUNSUBSCRIBE; - - getPubSubListeners(type: PubSubType) { - return this.#queue.getPubSubListeners(type); - } - - extendPubSubChannelListeners( - type: PubSubType, - channel: string, - listeners: ChannelListeners - ) { - return this.#pubSubCommand( - this.#queue.extendPubSubChannelListeners(type, channel, listeners) - ); - } - - extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { - return this.#pubSubCommand( - this.#queue.extendPubSubListeners(type, listeners) + promises.push( + this._queue.addCommand( + HELLO.transformArguments(this._options.RESP, hello), + { asap: true } + ) ); - } - - QUIT(): Promise { - return this.#socket.quit(async () => { - const quitPromise = this.#queue.addCommand(['QUIT']); - this.#tick(); - const [reply] = await Promise.all([ - quitPromise, - this.#destroyIsolationPool() - ]); - return reply; - }); - } - - quit = this.QUIT; - - #tick(force = false): void { - if (this.#socket.writableNeedDrain || (!force && !this.#socket.isReady)) { - return; + } else { + if (this._options?.name) { + // promises.push( + // this.#queue.addCommand( + // COMMANDS.CLIENT_SETNAME.transformArguments(this.#options.name), + // { asap: true } + // ) + // ); } - this.#socket.cork(); - - while (!this.#socket.writableNeedDrain) { - const args = this.#queue.getCommandToSend(); - if (args === undefined) break; - - this.#socket.writeCommand(args); + if (this._options?.username || this._options?.password) { + // promises.push( + // this.#queue.addCommand( + // COMMANDS.AUTH.transformArguments({ + // username: this.#options.username, + // password: this.#options.password ?? '' + // }), + // { asap: true } + // ) + // ); } - } + } - executeIsolated(fn: (client: RedisClientType) => T | Promise): Promise { - if (!this.#isolationPool) return Promise.reject(new ClientClosedError()); - return this.#isolationPool.use(fn); - } + const resubscribePromise = this._queue.resubscribe(); + if (resubscribePromise) { + promises.push(resubscribePromise); + } - MULTI(): RedisClientMultiCommandType { - return new (this as any).Multi( - this.multiExecutor.bind(this), - this.#options?.legacyMode - ); - } - - multi = this.MULTI; + if (promises.length) { + this._tick(true); + await Promise.all(promises); + } + }; - async multiExecutor( + return new RedisSocket(socketInitiator, this._options?.socket) + .on('data', chunk => this._queue.decoder.write(chunk)) + .on('error', err => { + this.emit('error', err); + if (this._socket.isOpen && !this._options?.disableOfflineQueue) { + this._queue.flushWaitingForReply(err); + } else { + this._queue.flushAll(err); + } + }) + .on('connect', () => this.emit('connect')) + .on('ready', () => { + this.emit('ready'); + this._setPingTimer(); + this._tick(); + }) + .on('reconnecting', () => this.emit('reconnecting')) + .on('drain', () => this._tick()) + .on('end', () => this.emit('end')); + } + + // #legacyMode(): void { + // if (!this.#options?.legacyMode) return; + + // (this as any).#v4.sendCommand = this.#sendCommand.bind(this); + // (this as any).sendCommand = (...args: Array): void => { + // const result = this.#legacySendCommand(...args); + // if (result) { + // result.promise + // .then(reply => result.callback(null, reply)) + // .catch(err => result.callback(err)); + // } + // }; + + // for (const [name, command] of Object.entries(COMMANDS)) { + // this.#defineLegacyCommand(name, command); + // (this as any)[name.toLowerCase()] ??= (this as any)[name]; + // } + + // // hard coded commands + // this.#defineLegacyCommand('SELECT'); + // this.#defineLegacyCommand('select'); + // this.#defineLegacyCommand('SUBSCRIBE'); + // this.#defineLegacyCommand('subscribe'); + // this.#defineLegacyCommand('PSUBSCRIBE'); + // this.#defineLegacyCommand('pSubscribe'); + // this.#defineLegacyCommand('UNSUBSCRIBE'); + // this.#defineLegacyCommand('unsubscribe'); + // this.#defineLegacyCommand('PUNSUBSCRIBE'); + // this.#defineLegacyCommand('pUnsubscribe'); + // this.#defineLegacyCommand('QUIT'); + // this.#defineLegacyCommand('quit'); + // } + + // #legacySendCommand(...args: Array) { + // const callback = typeof args[args.length - 1] === 'function' ? + // args.pop() as ClientLegacyCallback : + // undefined; + + // const promise = this.#sendCommand(transformLegacyCommandArguments(args)); + // if (callback) return { + // promise, + // callback + // }; + // promise.catch(err => this.emit('error', err)); + // } + + // #defineLegacyCommand(name: string, command?: RedisCommand): void { + // this.#v4[name] = (this as any)[name].bind(this); + // (this as any)[name] = command && command.TRANSFORM_LEGACY_REPLY && command.transformReply ? + // (...args: Array) => { + // const result = this.#legacySendCommand(name, ...args); + // if (result) { + // result.promise + // .then(reply => result.callback(null, command.transformReply!(reply))) + // .catch(err => result.callback(err)); + // } + // } : + // (...args: Array) => (this as any).sendCommand(name, ...args); + // } + + private _pingTimer?: NodeJS.Timer; + + private _setPingTimer(): void { + if (!this._options?.pingInterval || !this._socket.isReady) return; + clearTimeout(this._pingTimer); + + this._pingTimer = setTimeout(() => { + if (!this._socket.isReady) return; + + // using _sendCommand to support legacy mode + this._sendCommand(['PING']) + .then(reply => this.emit('ping-interval', reply)) + .catch(err => this.emit('error', err)) + .finally(() => this._setPingTimer()); + }, this._options.pingInterval); + } + + withCommandOptions(options: T) { + const proxy = Object.create(this.self); + proxy.commandOptions = options; + return proxy as RedisClientType< + M, + F, + S, + RESP, + T['flags'] extends Flags ? T['flags'] : {} + >; + } + + private _commandOptionsProxy< + K extends keyof ClientCommandOptions, + V extends ClientCommandOptions[K] + >( + key: K, + value: V + ) { + const proxy = Object.create(this.self); + proxy.commandOptions = Object.create((this as unknown as ProxyClient).commandOptions ?? null); + proxy.commandOptions[key] = value; + return proxy as RedisClientType< + M, + F, + S, + RESP, + K extends 'flags' ? V extends Flags ? V : {} : FLAGS + >; + } + + /** + * Override the `flags` command option + */ + withFlags(flags: FLAGS) { + return this._commandOptionsProxy('flags', flags); + } + + /** + * Override the `asap` command option to `true` + */ + asap() { + return this._commandOptionsProxy('asap', true); + } + + /** + * Override the `isolated` command option to `true` + */ + isolated() { + return this._commandOptionsProxy('isolated', true); + } + + duplicate(overrides?: Partial>) { + return new (Object.getPrototypeOf(this).constructor)({ + ...this._options, + ...overrides + }) as RedisClientType; + } + + async connect(): Promise { + await this._socket.connect(); + this.self._isolationPool = createPool({ + create: async () => { + const duplicate = this.duplicate({ + isolationPoolOptions: undefined + }).on('error', err => this.emit('error', err)); + await duplicate.connect(); + return duplicate; + }, + destroy: client => client.disconnect() + }, this._options?.isolationPoolOptions); + } + + sendCommand = this._sendCommand.bind(this); + + // using `_` to avoid conflicts with the legacy mode + _sendCommand( + args: CommandArguments, + options?: ClientCommandOptions + ): Promise { + if (!this._socket.isOpen) { + return Promise.reject(new ClientClosedError()); + } else if (options?.isolated) { + return this.executeIsolated(isolatedClient => + isolatedClient.sendCommand(args, { + ...options, + isolated: false + }) + ); + } else if (!this._socket.isReady && this._options?.disableOfflineQueue) { + return Promise.reject(new ClientOfflineError()); + } + + const promise = this._queue.addCommand(args, options); + this._tick(); + return promise; + } + + async SELECT(db: number): Promise { + await this._sendCommand(['SELECT', db.toString()]); + this._selectedDB = db; + } + + select = this.SELECT; + + private _pubSubCommand(promise: Promise | undefined) { + if (promise === undefined) return Promise.resolve(); + + this._tick(); + return promise; + } + + SUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + console.log('SUBSCRIBE', channels, listener, bufferMode, this._options?.RESP); + return this._pubSubCommand( + this._queue.subscribe( + PubSubType.CHANNELS, + channels, + listener, + bufferMode + ) + ); + } + + subscribe = this.SUBSCRIBE; + + UNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this._pubSubCommand( + this._queue.unsubscribe( + PubSubType.CHANNELS, + channels, + listener, + bufferMode + ) + ); + } + + unsubscribe = this.UNSUBSCRIBE; + + PSUBSCRIBE( + patterns: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + return this._pubSubCommand( + this._queue.subscribe( + PubSubType.PATTERNS, + patterns, + listener, + bufferMode + ) + ); + } + + pSubscribe = this.PSUBSCRIBE; + + PUNSUBSCRIBE( + patterns?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this._pubSubCommand( + this._queue.unsubscribe( + PubSubType.PATTERNS, + patterns, + listener, + bufferMode + ) + ); + } + + pUnsubscribe = this.PUNSUBSCRIBE; + + SSUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ): Promise { + return this._pubSubCommand( + this._queue.subscribe( + PubSubType.SHARDED, + channels, + listener, + bufferMode + ) + ); + } + + sSubscribe = this.SSUBSCRIBE; + + SUNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ): Promise { + return this._pubSubCommand( + this._queue.unsubscribe( + PubSubType.SHARDED, + channels, + listener, + bufferMode + ) + ); + } + + sUnsubscribe = this.SUNSUBSCRIBE; + + getPubSubListeners(type: PubSubType) { + return this._queue.getPubSubListeners(type); + } + + extendPubSubChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + return this._pubSubCommand( + this._queue.extendPubSubChannelListeners(type, channel, listeners) + ); + } + + extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners) { + return this._pubSubCommand( + this._queue.extendPubSubListeners(type, listeners) + ); + } + + QUIT(): Promise { + return this._socket.quit(async () => { + const quitPromise = this._queue.addCommand(['QUIT']); + this._tick(); + const [reply] = await Promise.all([ + quitPromise, + this._destroyIsolationPool() + ]); + return reply; + }); + } + + quit = this.QUIT; + + _tick(force = false): void { + if (this._socket.writableNeedDrain || (!force && !this._socket.isReady)) { + return; + } + + this._socket.cork(); + + while (!this._socket.writableNeedDrain) { + const args = this._queue.getCommandToSend(); + if (args === undefined) break; + + this._socket.writeCommand(args); + } + } + + executeIsolated(fn: (client: RedisClientType) => T | Promise): Promise { + return this._isolationPool ? + this._isolationPool.use(fn) : + Promise.reject(new ClientClosedError()); + } + + private _addMultiCommands( + commands: Array, + chainId?: symbol, + flags?: Flags + ) { + return Promise.all( + commands.map(({ args }) => this._queue.addCommand(args, { + chainId, + flags + })) + ); + } + + MULTI(): RedisClientMultiCommandType<[], M, F, S, RESP, FLAGS> { + return new (this as any).Multi( + async ( commands: Array, selectedDB?: number, chainId?: symbol - ): Promise> { - if (!this.#socket.isOpen) { - return Promise.reject(new ClientClosedError()); + ) => { + if (!this._socket.isOpen) { + return Promise.reject(new ClientClosedError()); } - const promise = chainId ? + const flags = (this as ProxyClient).commandOptions?.flags, + promise = chainId ? // if `chainId` has a value, it's a `MULTI` (and not "pipeline") - need to add the `MULTI` and `EXEC` commands Promise.all([ - this.#queue.addCommand(['MULTI'], { chainId }), - this.#addMultiCommands(commands, chainId), - this.#queue.addCommand(['EXEC'], { chainId }) + this._queue.addCommand(['MULTI'], { chainId }), + this._addMultiCommands(commands, chainId), + this._queue.addCommand(['EXEC'], { chainId, flags }) ]) : - this.#addMultiCommands(commands); + this._addMultiCommands(commands, undefined, flags); - this.#tick(); + this._tick(); const results = await promise; if (selectedDB !== undefined) { - this.#selectedDB = selectedDB; + this._selectedDB = selectedDB; } return results; - } - - #addMultiCommands(commands: Array, chainId?: symbol) { - return Promise.all( - commands.map(({ args }) => this.#queue.addCommand(args, { chainId })) - ); - } - - async* scanIterator(options?: ScanCommandOptions): AsyncIterable { - let cursor = 0; - do { - const reply = await (this as any).scan(cursor, options); - cursor = reply.cursor; - for (const key of reply.keys) { - yield key; - } - } while (cursor !== 0); - } - - async* hScanIterator(key: string, options?: ScanOptions): AsyncIterable> { - let cursor = 0; - do { - const reply = await (this as any).hScan(key, cursor, options); - cursor = reply.cursor; - for (const tuple of reply.tuples) { - yield tuple; - } - } while (cursor !== 0); - } - - async* sScanIterator(key: string, options?: ScanOptions): AsyncIterable { - let cursor = 0; - do { - const reply = await (this as any).sScan(key, cursor, options); - cursor = reply.cursor; - for (const member of reply.members) { - yield member; - } - } while (cursor !== 0); - } - - async* zScanIterator(key: string, options?: ScanOptions): AsyncIterable> { - let cursor = 0; - do { - const reply = await (this as any).zScan(key, cursor, options); - cursor = reply.cursor; - for (const member of reply.members) { - yield member; - } - } while (cursor !== 0); - } - - async disconnect(): Promise { - this.#queue.flushAll(new DisconnectsClientError()); - this.#socket.disconnect(); - await this.#destroyIsolationPool(); - } - - async #destroyIsolationPool(): Promise { - await this.#isolationPool!.drain(); - await this.#isolationPool!.clear(); - this.#isolationPool = undefined; - } - - ref(): void { - this.#socket.ref(); - } - - unref(): void { - this.#socket.unref(); - } + } + // self.#options?.legacyMode + ); + } + + multi = this.MULTI; + + async* scanIterator( + this: RedisClientType, + options?: ScanOptions & ScanIteratorOptions + ): AsyncIterable['keys'], FLAGS>> { + let cursor = options?.cursor ?? 0; + do { + const reply = await this.scan(cursor, options); + cursor = reply.cursor; + yield reply.keys; + } while (cursor !== 0); + } + + async* hScanIterator( + this: RedisClientType, + key: RedisArgument, + options?: ScanCommonOptions & ScanIteratorOptions + ) { + let cursor = options?.cursor ?? 0; + do { + const reply = await this.hScan(key, cursor, options); + cursor = reply.cursor; + yield reply.entries; + } while (cursor !== 0); + } + + async* sScanIterator( + this: RedisClientType, + key: RedisArgument, + options?: ScanCommonOptions & ScanIteratorOptions + ) { + let cursor = options?.cursor ?? 0; + do { + const reply = await this.sScan(key, cursor, options); + cursor = reply.cursor; + yield reply.members; + } while (cursor !== 0); + } + + async* zScanIterator( + this: RedisClientType, + key: RedisArgument, + options?: ScanCommonOptions & ScanIteratorOptions + ) { + let cursor = options?.cursor ?? 0; + do { + const reply = await this.zScan(key, cursor, options); + cursor = reply.cursor; + yield reply.members; + } while (cursor !== 0); + } + + async disconnect(): Promise { + this._queue.flushAll(new DisconnectsClientError()); + this._socket.disconnect(); + await this._destroyIsolationPool(); + } + + private async _destroyIsolationPool(): Promise { + await this._isolationPool!.drain(); + await this._isolationPool!.clear(); + this.self._isolationPool = undefined; + } + + ref(): void { + this._socket.ref(); + } + + unref(): void { + this._socket.unref(); + } } - -attachCommands({ - BaseClass: RedisClient, - commands: COMMANDS, - executor: RedisClient.prototype.commandsExecutor -}); -(RedisClient.prototype as any).Multi = RedisClientMultiCommand; diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index e347667bf2c..3503a34af2b 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -1,200 +1,259 @@ -import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, ExcludeMappedString, RedisFunction, RedisCommands } from '../commands'; +import COMMANDS from '../commands'; import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; -import { attachCommands, attachExtensions, transformLegacyCommandArguments } from '../commander'; +import { ReplyWithFlags, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, Flags, ReplyUnion } from '../RESP/types'; +import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander'; type CommandSignature< - C extends RedisCommand, - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = (...args: Parameters) => RedisClientMultiCommandType; + REPLIES extends Array, + C extends Command, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + FLAGS extends Flags +> = (...args: Parameters) => RedisClientMultiCommandType< + [...REPLIES, ReplyWithFlags, FLAGS>], + M, + F, + S, + RESP, + FLAGS +>; type WithCommands< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + FLAGS extends Flags > = { - [P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], M, F, S>; + [P in keyof typeof COMMANDS]: CommandSignature; }; type WithModules< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + FLAGS extends Flags > = { - [P in keyof M as ExcludeMappedString

]: { - [C in keyof M[P] as ExcludeMappedString]: CommandSignature; - }; + [P in keyof M]: { + [C in keyof M[P]]: CommandSignature; + }; }; type WithFunctions< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + FLAGS extends Flags > = { - [P in keyof F as ExcludeMappedString

]: { - [FF in keyof F[P] as ExcludeMappedString]: CommandSignature; - }; + [L in keyof F]: { + [C in keyof F[L]]: CommandSignature; + }; }; type WithScripts< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + FLAGS extends Flags > = { - [P in keyof S as ExcludeMappedString

]: CommandSignature; + [P in keyof S]: CommandSignature; }; export type RedisClientMultiCommandType< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = RedisClientMultiCommand & WithCommands & WithModules & WithFunctions & WithScripts; + REPLIES extends Array, + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + FLAGS extends Flags +> = ( + RedisClientMultiCommand & + WithCommands & + WithModules & + WithFunctions & + WithScripts +); + +type MULTI_REPLY = { + GENERIC: 'generic'; + TYPED: 'typed'; +}; -type InstantiableRedisMultiCommand< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = new (...args: ConstructorParameters) => RedisClientMultiCommandType; +type MultiReply = MULTI_REPLY[keyof MULTI_REPLY]; + +type ReplyType = T extends MULTI_REPLY['TYPED'] ? REPLIES : Array; export type RedisClientMultiExecutor = ( - queue: Array, - selectedDB?: number, - chainId?: symbol -) => Promise>; - -export default class RedisClientMultiCommand { - static extend< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(extensions?: RedisExtensions): InstantiableRedisMultiCommand { - return attachExtensions({ - BaseClass: RedisClientMultiCommand, - modulesExecutor: RedisClientMultiCommand.prototype.commandsExecutor, - modules: extensions?.modules, - functionsExecutor: RedisClientMultiCommand.prototype.functionsExecutor, - functions: extensions?.functions, - scriptsExecutor: RedisClientMultiCommand.prototype.scriptsExecutor, - scripts: extensions?.scripts - }); - } - - readonly #multi = new RedisMultiCommand(); - readonly #executor: RedisClientMultiExecutor; - readonly v4: Record = {}; - #selectedDB?: number; - - constructor(executor: RedisClientMultiExecutor, legacyMode = false) { - this.#executor = executor; - if (legacyMode) { - this.#legacyMode(); - } - } - - #legacyMode(): void { - this.v4.addCommand = this.addCommand.bind(this); - (this as any).addCommand = (...args: Array): this => { - this.#multi.addCommand(transformLegacyCommandArguments(args)); - return this; - }; - this.v4.exec = this.exec.bind(this); - (this as any).exec = (callback?: (err: Error | null, replies?: Array) => unknown): void => { - this.v4.exec() - .then((reply: Array) => { - if (!callback) return; - - callback(null, reply); - }) - .catch((err: Error) => { - if (!callback) { - // this.emit('error', err); - return; - } - - callback(err); - }); - }; - - for (const [ name, command ] of Object.entries(COMMANDS as RedisCommands)) { - this.#defineLegacyCommand(name, command); - (this as any)[name.toLowerCase()] ??= (this as any)[name]; - } - } - - #defineLegacyCommand(this: any, name: string, command?: RedisCommand): void { - this.v4[name] = this[name].bind(this.v4); - this[name] = command && command.TRANSFORM_LEGACY_REPLY && command.transformReply ? - (...args: Array) => { - this.#multi.addCommand( - [name, ...transformLegacyCommandArguments(args)], - command.transformReply - ); - return this; - } : - (...args: Array) => this.addCommand(name, ...args); - } - - commandsExecutor(command: RedisCommand, args: Array): this { - return this.addCommand( - command.transformArguments(...args), - command.transformReply - ); - } - - SELECT(db: number, transformReply?: RedisCommand['transformReply']): this { - this.#selectedDB = db; - return this.addCommand(['SELECT', db.toString()], transformReply); - } - - select = this.SELECT; - - addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): this { - this.#multi.addCommand(args, transformReply); - return this; - } - - functionsExecutor(fn: RedisFunction, args: Array, name: string): this { - this.#multi.addFunction(name, fn, args); - return this; - } - - scriptsExecutor(script: RedisScript, args: Array): this { - this.#multi.addScript(script, args); - return this; - } - - async exec(execAsPipeline = false): Promise> { - if (execAsPipeline) { - return this.execAsPipeline(); - } - - return this.#multi.handleExecReplies( - await this.#executor( - this.#multi.queue, - this.#selectedDB, - RedisMultiCommand.generateChainId() - ) - ); - } - - EXEC = this.exec; - - async execAsPipeline(): Promise> { - if (this.#multi.queue.length === 0) return []; - - return this.#multi.transformReplies( - await this.#executor( - this.#multi.queue, - this.#selectedDB - ) - ); - } -} + queue: Array, + selectedDB?: number, + chainId?: symbol +) => Promise>; + +export default class RedisClientMultiCommand extends RedisMultiCommand { + static #createCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return function (this: RedisClientMultiCommand) { + return this.addCommand( + command.transformArguments.apply(undefined, arguments as any), + transformReply + ); + }; + } + + static #createModuleCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return function (this: { self: RedisClientMultiCommand }) { + return this.self.addCommand( + command.transformArguments.apply(undefined, arguments as any), + transformReply + ); + }; + } + + static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { + const prefix = functionArgumentsPrefix(name, fn), + transformReply = getTransformReply(fn, resp); + return function (this: { self: RedisClientMultiCommand }) { + const fnArgs = fn.transformArguments.apply(undefined, arguments as any), + args: CommandArguments = prefix.concat(fnArgs); + args.preserve = fnArgs.preserve; + return this.self.addCommand( + args, + transformReply + ); + }; + } + + static #createScriptCommand(script: RedisScript, resp: RespVersions) { + const transformReply = getTransformReply(script, resp); + return function (this: RedisClientMultiCommand) { + return this.addScript( + script, + script.transformArguments.apply(undefined, arguments as any), + transformReply + ); + }; + } + + static extend< + M extends RedisModules = Record, + F extends RedisFunctions = Record, + S extends RedisScripts = Record, + RESP extends RespVersions = 2 + >(config?: CommanderConfig) { + return attachConfig({ + BaseClass: RedisClientMultiCommand, + commands: COMMANDS, + createCommand: RedisClientMultiCommand.#createCommand, + createModuleCommand: RedisClientMultiCommand.#createModuleCommand, + createFunctionCommand: RedisClientMultiCommand.#createFunctionCommand, + createScriptCommand: RedisClientMultiCommand.#createScriptCommand, + config + }); + } + + // readonly #multi = new RedisMultiCommand(); + readonly #executor: RedisClientMultiExecutor; + // readonly v4: Record = {}; + #selectedDB?: number; -attachCommands({ - BaseClass: RedisClientMultiCommand, - commands: COMMANDS, - executor: RedisClientMultiCommand.prototype.commandsExecutor -}); + constructor(executor: RedisClientMultiExecutor, legacyMode = false) { + super(); + this.#executor = executor; + // if (legacyMode) { + // this.#legacyMode(); + // } + } + + // #legacyMode(): void { + // this.v4.addCommand = this.addCommand.bind(this); + // (this as any).addCommand = (...args: Array): this => { + // this.#multi.addCommand(transformLegacyCommandArguments(args)); + // return this; + // }; + // this.v4.exec = this.exec.bind(this); + // (this as any).exec = (callback?: (err: Error | null, replies?: Array) => unknown): void => { + // this.v4.exec() + // .then((reply: Array) => { + // if (!callback) return; + + // callback(null, reply); + // }) + // .catch((err: Error) => { + // if (!callback) { + // // this.emit('error', err); + // return; + // } + + // callback(err); + // }); + // }; + + // for (const [name, command] of Object.entries(COMMANDS as RedisCommands)) { + // this.#defineLegacyCommand(name, command); + // (this as any)[name.toLowerCase()] ??= (this as any)[name]; + // } + // } + + // #defineLegacyCommand(this: any, name: string, command?: RedisCommand): void { + // this.v4[name] = this[name].bind(this.v4); + // this[name] = command && command.TRANSFORM_LEGACY_REPLY && command.transformReply ? + // (...args: Array) => { + // this.#multi.addCommand( + // [name, ...transformLegacyCommandArguments(args)], + // command.transformReply + // ); + // return this; + // } : + // (...args: Array) => this.addCommand(name, ...args); + // } + + SELECT(db: number, transformReply?: TransformReply): this { + this.#selectedDB = db; + return this.addCommand(['SELECT', db.toString()], transformReply); + } + + select = this.SELECT; + + async exec(execAsPipeline = false) { + if (execAsPipeline) return this.execAsPipeline(); + + return this.handleExecReplies( + await this.#executor( + this.queue, + this.#selectedDB, + RedisMultiCommand.generateChainId() + ) + ) as ReplyType; + } + + EXEC = this.exec; + + execTyped(execAsPipeline = false) { + return this.exec(execAsPipeline); + } + + async execAsPipeline() { + if (this.queue.length === 0) return [] as ReplyType; + + return this.transformReplies( + await this.#executor( + this.queue, + this.#selectedDB + ) + ) as ReplyType; + } + + execAsPipelineTyped() { + return this.execAsPipeline(); + } +} diff --git a/packages/client/lib/client/pub-sub.ts b/packages/client/lib/client/pub-sub.ts index a8a909e0252..afa7972e831 100644 --- a/packages/client/lib/client/pub-sub.ts +++ b/packages/client/lib/client/pub-sub.ts @@ -1,37 +1,37 @@ -import { RedisCommandArgument } from "../commands"; +import { RedisArgument } from "../RESP/types"; export enum PubSubType { - CHANNELS = 'CHANNELS', - PATTERNS = 'PATTERNS', - SHARDED = 'SHARDED' + CHANNELS = 'CHANNELS', + PATTERNS = 'PATTERNS', + SHARDED = 'SHARDED' } const COMMANDS = { - [PubSubType.CHANNELS]: { - subscribe: Buffer.from('subscribe'), - unsubscribe: Buffer.from('unsubscribe'), - message: Buffer.from('message') - }, - [PubSubType.PATTERNS]: { - subscribe: Buffer.from('psubscribe'), - unsubscribe: Buffer.from('punsubscribe'), - message: Buffer.from('pmessage') - }, - [PubSubType.SHARDED]: { - subscribe: Buffer.from('ssubscribe'), - unsubscribe: Buffer.from('sunsubscribe'), - message: Buffer.from('smessage') - } + [PubSubType.CHANNELS]: { + subscribe: Buffer.from('subscribe'), + unsubscribe: Buffer.from('unsubscribe'), + message: Buffer.from('message') + }, + [PubSubType.PATTERNS]: { + subscribe: Buffer.from('psubscribe'), + unsubscribe: Buffer.from('punsubscribe'), + message: Buffer.from('pmessage') + }, + [PubSubType.SHARDED]: { + subscribe: Buffer.from('ssubscribe'), + unsubscribe: Buffer.from('sunsubscribe'), + message: Buffer.from('smessage') + } }; export type PubSubListener< - RETURN_BUFFERS extends boolean = false + RETURN_BUFFERS extends boolean = false > = (message: T, channel: T) => unknown; export interface ChannelListeners { - unsubscribing: boolean; - buffers: Set>; - strings: Set>; + unsubscribing: boolean; + buffers: Set>; + strings: Set>; } export type PubSubTypeListeners = Map; @@ -39,370 +39,370 @@ export type PubSubTypeListeners = Map; type Listeners = Record; export type PubSubCommand = ReturnType< - typeof PubSub.prototype.subscribe | - typeof PubSub.prototype.unsubscribe | - typeof PubSub.prototype.extendTypeListeners + typeof PubSub.prototype.subscribe | + typeof PubSub.prototype.unsubscribe | + typeof PubSub.prototype.extendTypeListeners >; export class PubSub { - static isStatusReply(reply: Array): boolean { - return ( - COMMANDS[PubSubType.CHANNELS].subscribe.equals(reply[0]) || - COMMANDS[PubSubType.CHANNELS].unsubscribe.equals(reply[0]) || - COMMANDS[PubSubType.PATTERNS].subscribe.equals(reply[0]) || - COMMANDS[PubSubType.PATTERNS].unsubscribe.equals(reply[0]) || - COMMANDS[PubSubType.SHARDED].subscribe.equals(reply[0]) - ); - } - - static isShardedUnsubscribe(reply: Array): boolean { - return COMMANDS[PubSubType.SHARDED].unsubscribe.equals(reply[0]); - } - - static #channelsArray(channels: string | Array) { - return (Array.isArray(channels) ? channels : [channels]); + static isStatusReply(reply: Array): boolean { + return ( + COMMANDS[PubSubType.CHANNELS].subscribe.equals(reply[0]) || + COMMANDS[PubSubType.CHANNELS].unsubscribe.equals(reply[0]) || + COMMANDS[PubSubType.PATTERNS].subscribe.equals(reply[0]) || + COMMANDS[PubSubType.PATTERNS].unsubscribe.equals(reply[0]) || + COMMANDS[PubSubType.SHARDED].subscribe.equals(reply[0]) + ); + } + + static isShardedUnsubscribe(reply: Array): boolean { + return COMMANDS[PubSubType.SHARDED].unsubscribe.equals(reply[0]); + } + + static #channelsArray(channels: string | Array) { + return (Array.isArray(channels) ? channels : [channels]); + } + + static #listenersSet( + listeners: ChannelListeners, + returnBuffers?: T + ) { + return (returnBuffers ? listeners.buffers : listeners.strings); + } + + #subscribing = 0; + + #isActive = false; + + get isActive() { + return this.#isActive; + } + + #listeners: Listeners = { + [PubSubType.CHANNELS]: new Map(), + [PubSubType.PATTERNS]: new Map(), + [PubSubType.SHARDED]: new Map() + }; + + subscribe( + type: PubSubType, + channels: string | Array, + listener: PubSubListener, + returnBuffers?: T + ) { + const args: Array = [COMMANDS[type].subscribe], + channelsArray = PubSub.#channelsArray(channels); + for (const channel of channelsArray) { + let channelListeners = this.#listeners[type].get(channel); + if (!channelListeners || channelListeners.unsubscribing) { + args.push(channel); + } } - static #listenersSet( - listeners: ChannelListeners, - returnBuffers?: T - ) { - return (returnBuffers ? listeners.buffers : listeners.strings); + if (args.length === 1) { + // all channels are already subscribed, add listeners without issuing a command + for (const channel of channelsArray) { + PubSub.#listenersSet( + this.#listeners[type].get(channel)!, + returnBuffers + ).add(listener); + } + return; } - #subscribing = 0; - - #isActive = false; - - get isActive() { - return this.#isActive; - } - - #listeners: Listeners = { - [PubSubType.CHANNELS]: new Map(), - [PubSubType.PATTERNS]: new Map(), - [PubSubType.SHARDED]: new Map() - }; - - subscribe( - type: PubSubType, - channels: string | Array, - listener: PubSubListener, - returnBuffers?: T - ) { - const args: Array = [COMMANDS[type].subscribe], - channelsArray = PubSub.#channelsArray(channels); + this.#isActive = true; + this.#subscribing++; + return { + args, + channelsCounter: args.length - 1, + resolve: () => { + this.#subscribing--; for (const channel of channelsArray) { - let channelListeners = this.#listeners[type].get(channel); - if (!channelListeners || channelListeners.unsubscribing) { - args.push(channel); - } - } + let listeners = this.#listeners[type].get(channel); + if (!listeners) { + listeners = { + unsubscribing: false, + buffers: new Set(), + strings: new Set() + }; + this.#listeners[type].set(channel, listeners); + } - if (args.length === 1) { - // all channels are already subscribed, add listeners without issuing a command - for (const channel of channelsArray) { - PubSub.#listenersSet( - this.#listeners[type].get(channel)!, - returnBuffers - ).add(listener); - } - return; + PubSub.#listenersSet(listeners, returnBuffers).add(listener); } - - this.#isActive = true; - this.#subscribing++; - return { - args, - channelsCounter: args.length - 1, - resolve: () => { - this.#subscribing--; - for (const channel of channelsArray) { - let listeners = this.#listeners[type].get(channel); - if (!listeners) { - listeners = { - unsubscribing: false, - buffers: new Set(), - strings: new Set() - }; - this.#listeners[type].set(channel, listeners); - } - - PubSub.#listenersSet(listeners, returnBuffers).add(listener); - } - }, - reject: () => { - this.#subscribing--; - this.#updateIsActive(); - } - }; + }, + reject: () => { + this.#subscribing--; + this.#updateIsActive(); + } + }; + } + + extendChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + if (!this.#extendChannelListeners(type, channel, listeners)) return; + + this.#isActive = true; + this.#subscribing++; + return { + args: [ + COMMANDS[type].subscribe, + channel + ], + channelsCounter: 1, + resolve: () => this.#subscribing--, + reject: () => { + this.#subscribing--; + this.#updateIsActive(); + } + }; + } + + #extendChannelListeners( + type: PubSubType, + channel: string, + listeners: ChannelListeners + ) { + const existingListeners = this.#listeners[type].get(channel); + if (!existingListeners) { + this.#listeners[type].set(channel, listeners); + return true; } - extendChannelListeners( - type: PubSubType, - channel: string, - listeners: ChannelListeners - ) { - if (!this.#extendChannelListeners(type, channel, listeners)) return; - - this.#isActive = true; - this.#subscribing++; - return { - args: [ - COMMANDS[type].subscribe, - channel - ], - channelsCounter: 1, - resolve: () => this.#subscribing--, - reject: () => { - this.#subscribing--; - this.#updateIsActive(); - } - }; + for (const listener of listeners.buffers) { + existingListeners.buffers.add(listener); } - #extendChannelListeners( - type: PubSubType, - channel: string, - listeners: ChannelListeners - ) { - const existingListeners = this.#listeners[type].get(channel); - if (!existingListeners) { - this.#listeners[type].set(channel, listeners); - return true; - } - - for (const listener of listeners.buffers) { - existingListeners.buffers.add(listener); - } - - for (const listener of listeners.strings) { - existingListeners.strings.add(listener); - } - - return false; + for (const listener of listeners.strings) { + existingListeners.strings.add(listener); } - extendTypeListeners(type: PubSubType, listeners: PubSubTypeListeners) { - const args: Array = [COMMANDS[type].subscribe]; - for (const [channel, channelListeners] of listeners) { - if (this.#extendChannelListeners(type, channel, channelListeners)) { - args.push(channel); - } - } + return false; + } - if (args.length === 1) return; - - this.#isActive = true; - this.#subscribing++; - return { - args, - channelsCounter: args.length - 1, - resolve: () => this.#subscribing--, - reject: () => { - this.#subscribing--; - this.#updateIsActive(); - } - }; + extendTypeListeners(type: PubSubType, listeners: PubSubTypeListeners) { + const args: Array = [COMMANDS[type].subscribe]; + for (const [channel, channelListeners] of listeners) { + if (this.#extendChannelListeners(type, channel, channelListeners)) { + args.push(channel); + } } - unsubscribe( - type: PubSubType, - channels?: string | Array, - listener?: PubSubListener, - returnBuffers?: T - ) { - const listeners = this.#listeners[type]; - if (!channels) { - return this.#unsubscribeCommand( - [COMMANDS[type].unsubscribe], - // cannot use `this.#subscribed` because there might be some `SUBSCRIBE` commands in the queue - // cannot use `this.#subscribed + this.#subscribing` because some `SUBSCRIBE` commands might fail - NaN, - () => listeners.clear() - ); - } + if (args.length === 1) return; - const channelsArray = PubSub.#channelsArray(channels); - if (!listener) { - return this.#unsubscribeCommand( - [COMMANDS[type].unsubscribe, ...channelsArray], - channelsArray.length, - () => { - for (const channel of channelsArray) { - listeners.delete(channel); - } - } - ); - } + this.#isActive = true; + this.#subscribing++; + return { + args, + channelsCounter: args.length - 1, + resolve: () => this.#subscribing--, + reject: () => { + this.#subscribing--; + this.#updateIsActive(); + } + }; + } + + unsubscribe( + type: PubSubType, + channels?: string | Array, + listener?: PubSubListener, + returnBuffers?: T + ) { + const listeners = this.#listeners[type]; + if (!channels) { + return this.#unsubscribeCommand( + [COMMANDS[type].unsubscribe], + // cannot use `this.#subscribed` because there might be some `SUBSCRIBE` commands in the queue + // cannot use `this.#subscribed + this.#subscribing` because some `SUBSCRIBE` commands might fail + NaN, + () => listeners.clear() + ); + } - const args: Array = [COMMANDS[type].unsubscribe]; - for (const channel of channelsArray) { - const sets = listeners.get(channel); - if (sets) { - let current, - other; - if (returnBuffers) { - current = sets.buffers; - other = sets.strings; - } else { - current = sets.strings; - other = sets.buffers; - } - - const currentSize = current.has(listener) ? current.size - 1 : current.size; - if (currentSize !== 0 || other.size !== 0) continue; - sets.unsubscribing = true; - } - - args.push(channel); + const channelsArray = PubSub.#channelsArray(channels); + if (!listener) { + return this.#unsubscribeCommand( + [COMMANDS[type].unsubscribe, ...channelsArray], + channelsArray.length, + () => { + for (const channel of channelsArray) { + listeners.delete(channel); + } } + ); + } - if (args.length === 1) { - // all channels has other listeners, - // delete the listeners without issuing a command - for (const channel of channelsArray) { - PubSub.#listenersSet( - listeners.get(channel)!, - returnBuffers - ).delete(listener); - } - return; + const args: Array = [COMMANDS[type].unsubscribe]; + for (const channel of channelsArray) { + const sets = listeners.get(channel); + if (sets) { + let current, + other; + if (returnBuffers) { + current = sets.buffers; + other = sets.strings; + } else { + current = sets.strings; + other = sets.buffers; } - return this.#unsubscribeCommand( - args, - args.length - 1, - () => { - for (const channel of channelsArray) { - const sets = listeners.get(channel); - if (!sets) continue; - - (returnBuffers ? sets.buffers : sets.strings).delete(listener); - if (sets.buffers.size === 0 && sets.strings.size === 0) { - listeners.delete(channel); - } - } - } - ); - } + const currentSize = current.has(listener) ? current.size - 1 : current.size; + if (currentSize !== 0 || other.size !== 0) continue; + sets.unsubscribing = true; + } - #unsubscribeCommand( - args: Array, - channelsCounter: number, - removeListeners: () => void - ) { - return { - args, - channelsCounter, - resolve: () => { - removeListeners(); - this.#updateIsActive(); - }, - reject: undefined // use the same structure as `subscribe` - }; + args.push(channel); } - #updateIsActive() { - this.#isActive = ( - this.#listeners[PubSubType.CHANNELS].size !== 0 || - this.#listeners[PubSubType.PATTERNS].size !== 0 || - this.#listeners[PubSubType.SHARDED].size !== 0 || - this.#subscribing !== 0 - ); + if (args.length === 1) { + // all channels has other listeners, + // delete the listeners without issuing a command + for (const channel of channelsArray) { + PubSub.#listenersSet( + listeners.get(channel)!, + returnBuffers + ).delete(listener); + } + return; } - reset() { - this.#isActive = false; - this.#subscribing = 0; - } + return this.#unsubscribeCommand( + args, + args.length - 1, + () => { + for (const channel of channelsArray) { + const sets = listeners.get(channel); + if (!sets) continue; - resubscribe(): Array { - const commands = []; - for (const [type, listeners] of Object.entries(this.#listeners)) { - if (!listeners.size) continue; - - this.#isActive = true; - this.#subscribing++; - const callback = () => this.#subscribing--; - commands.push({ - args: [ - COMMANDS[type as PubSubType].subscribe, - ...listeners.keys() - ], - channelsCounter: listeners.size, - resolve: callback, - reject: callback - }); + (returnBuffers ? sets.buffers : sets.strings).delete(listener); + if (sets.buffers.size === 0 && sets.strings.size === 0) { + listeners.delete(channel); + } } - - return commands; + } + ); + } + + #unsubscribeCommand( + args: Array, + channelsCounter: number, + removeListeners: () => void + ) { + return { + args, + channelsCounter, + resolve: () => { + removeListeners(); + this.#updateIsActive(); + }, + reject: undefined // use the same structure as `subscribe` + }; + } + + #updateIsActive() { + this.#isActive = ( + this.#listeners[PubSubType.CHANNELS].size !== 0 || + this.#listeners[PubSubType.PATTERNS].size !== 0 || + this.#listeners[PubSubType.SHARDED].size !== 0 || + this.#subscribing !== 0 + ); + } + + reset() { + this.#isActive = false; + this.#subscribing = 0; + } + + resubscribe(): Array { + const commands = []; + for (const [type, listeners] of Object.entries(this.#listeners)) { + if (!listeners.size) continue; + + this.#isActive = true; + this.#subscribing++; + const callback = () => this.#subscribing--; + commands.push({ + args: [ + COMMANDS[type as PubSubType].subscribe, + ...listeners.keys() + ], + channelsCounter: listeners.size, + resolve: callback, + reject: callback + }); } - handleMessageReply(reply: Array): boolean { - if (COMMANDS[PubSubType.CHANNELS].message.equals(reply[0])) { - this.#emitPubSubMessage( - PubSubType.CHANNELS, - reply[2], - reply[1] - ); - return true; - } else if (COMMANDS[PubSubType.PATTERNS].message.equals(reply[0])) { - this.#emitPubSubMessage( - PubSubType.PATTERNS, - reply[3], - reply[2], - reply[1] - ); - return true; - } else if (COMMANDS[PubSubType.SHARDED].message.equals(reply[0])) { - this.#emitPubSubMessage( - PubSubType.SHARDED, - reply[2], - reply[1] - ); - return true; - } - - return false; + return commands; + } + + handleMessageReply(reply: Array): boolean { + if (COMMANDS[PubSubType.CHANNELS].message.equals(reply[0])) { + this.#emitPubSubMessage( + PubSubType.CHANNELS, + reply[2], + reply[1] + ); + return true; + } else if (COMMANDS[PubSubType.PATTERNS].message.equals(reply[0])) { + this.#emitPubSubMessage( + PubSubType.PATTERNS, + reply[3], + reply[2], + reply[1] + ); + return true; + } else if (COMMANDS[PubSubType.SHARDED].message.equals(reply[0])) { + this.#emitPubSubMessage( + PubSubType.SHARDED, + reply[2], + reply[1] + ); + return true; } - removeShardedListeners(channel: string): ChannelListeners { - const listeners = this.#listeners[PubSubType.SHARDED].get(channel)!; - this.#listeners[PubSubType.SHARDED].delete(channel); - this.#updateIsActive(); - return listeners; + return false; + } + + removeShardedListeners(channel: string): ChannelListeners { + const listeners = this.#listeners[PubSubType.SHARDED].get(channel)!; + this.#listeners[PubSubType.SHARDED].delete(channel); + this.#updateIsActive(); + return listeners; + } + + #emitPubSubMessage( + type: PubSubType, + message: Buffer, + channel: Buffer, + pattern?: Buffer + ): void { + const keyString = (pattern ?? channel).toString(), + listeners = this.#listeners[type].get(keyString); + + if (!listeners) return; + + for (const listener of listeners.buffers) { + listener(message, channel); } - - #emitPubSubMessage( - type: PubSubType, - message: Buffer, - channel: Buffer, - pattern?: Buffer - ): void { - const keyString = (pattern ?? channel).toString(), - listeners = this.#listeners[type].get(keyString); - - if (!listeners) return; - - for (const listener of listeners.buffers) { - listener(message, channel); - } - if (!listeners.strings.size) return; + if (!listeners.strings.size) return; - const channelString = pattern ? channel.toString() : keyString, - messageString = channelString === '__redis__:invalidate' ? - // https://github.com/redis/redis/pull/7469 - // https://github.com/redis/redis/issues/7463 - (message === null ? null : (message as any as Array).map(x => x.toString())) as any : - message.toString(); - for (const listener of listeners.strings) { - listener(messageString, channelString); - } + const channelString = pattern ? channel.toString() : keyString, + messageString = channelString === '__redis__:invalidate' ? + // https://github.com/redis/redis/pull/7469 + // https://github.com/redis/redis/issues/7463 + (message === null ? null : (message as any as Array).map(x => x.toString())) as any : + message.toString(); + for (const listener of listeners.strings) { + listener(messageString, channelString); } + } - getTypeListeners(type: PubSubType): PubSubTypeListeners { - return this.#listeners[type]; - } + getTypeListeners(type: PubSubType): PubSubTypeListeners { + return this.#listeners[type]; + } } diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index 89533350f59..d0c1cfae896 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -1,309 +1,309 @@ import { EventEmitter } from 'events'; import * as net from 'net'; import * as tls from 'tls'; -import { RedisCommandArguments } from '../commands'; import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, ReconnectStrategyError } from '../errors'; import { promiseTimeout } from '../utils'; +import { RedisArgument } from '../RESP/types'; export interface RedisSocketCommonOptions { - /** - * Connection Timeout (in milliseconds) - */ - connectTimeout?: number; - /** - * Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) - */ - noDelay?: boolean; - /** - * Toggle [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) - */ - keepAlive?: number | false; - /** - * When the socket closes unexpectedly (without calling `.quit()`/`.disconnect()`), the client uses `reconnectStrategy` to decide what to do. The following values are supported: - * 1. `false` -> do not reconnect, close the client and flush the command queue. - * 2. `number` -> wait for `X` milliseconds before reconnecting. - * 3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error. - * Defaults to `retries => Math.min(retries * 50, 500)` - */ - reconnectStrategy?: false | number | ((retries: number, cause: Error) => false | Error | number); + /** + * Connection Timeout (in milliseconds) + */ + connectTimeout?: number; + /** + * Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) + */ + noDelay?: boolean; + /** + * Toggle [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) + */ + keepAlive?: number | false; + /** + * When the socket closes unexpectedly (without calling `.quit()`/`.disconnect()`), the client uses `reconnectStrategy` to decide what to do. The following values are supported: + * 1. `false` -> do not reconnect, close the client and flush the command queue. + * 2. `number` -> wait for `X` milliseconds before reconnecting. + * 3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error. + * Defaults to `retries => Math.min(retries * 50, 500)` + */ + reconnectStrategy?: false | number | ((retries: number, cause: Error) => false | Error | number); } type RedisNetSocketOptions = Partial & { - tls?: false; + tls?: false; }; export interface RedisTlsSocketOptions extends tls.ConnectionOptions { - tls: true; + tls: true; } export type RedisSocketOptions = RedisSocketCommonOptions & (RedisNetSocketOptions | RedisTlsSocketOptions); interface CreateSocketReturn { - connectEvent: string; - socket: T; + connectEvent: string; + socket: T; } export type RedisSocketInitiator = () => Promise; export default class RedisSocket extends EventEmitter { - static #initiateOptions(options?: RedisSocketOptions): RedisSocketOptions { - options ??= {}; - if (!(options as net.IpcSocketConnectOpts).path) { - (options as net.TcpSocketConnectOpts).port ??= 6379; - (options as net.TcpSocketConnectOpts).host ??= 'localhost'; - } - - options.connectTimeout ??= 5000; - options.keepAlive ??= 5000; - options.noDelay ??= true; - - return options; + static #initiateOptions(options?: RedisSocketOptions): RedisSocketOptions { + options ??= {}; + if (!(options as net.IpcSocketConnectOpts).path) { + (options as net.TcpSocketConnectOpts).port ??= 6379; + (options as net.TcpSocketConnectOpts).host ??= 'localhost'; } - static #isTlsSocket(options: RedisSocketOptions): options is RedisTlsSocketOptions { - return (options as RedisTlsSocketOptions).tls === true; - } + options.connectTimeout ??= 5000; + options.keepAlive ??= 5000; + options.noDelay ??= true; - readonly #initiator: RedisSocketInitiator; + return options; + } - readonly #options: RedisSocketOptions; + static #isTlsSocket(options: RedisSocketOptions): options is RedisTlsSocketOptions { + return (options as RedisTlsSocketOptions).tls === true; + } - #socket?: net.Socket | tls.TLSSocket; + readonly #initiator: RedisSocketInitiator; - #isOpen = false; + readonly #options: RedisSocketOptions; - get isOpen(): boolean { - return this.#isOpen; - } + #socket?: net.Socket | tls.TLSSocket; - #isReady = false; + #isOpen = false; - get isReady(): boolean { - return this.#isReady; - } + get isOpen(): boolean { + return this.#isOpen; + } - // `writable.writableNeedDrain` was added in v15.2.0 and therefore can't be used - // https://nodejs.org/api/stream.html#stream_writable_writableneeddrain - #writableNeedDrain = false; + #isReady = false; - get writableNeedDrain(): boolean { - return this.#writableNeedDrain; - } + get isReady(): boolean { + return this.#isReady; + } - #isSocketUnrefed = false; + // `writable.writableNeedDrain` was added in v15.2.0 and therefore can't be used + // https://nodejs.org/api/stream.html#stream_writable_writableneeddrain + #writableNeedDrain = false; - constructor(initiator: RedisSocketInitiator, options?: RedisSocketOptions) { - super(); + get writableNeedDrain(): boolean { + return this.#writableNeedDrain; + } - this.#initiator = initiator; - this.#options = RedisSocket.#initiateOptions(options); - } + #isSocketUnrefed = false; - #reconnectStrategy(retries: number, cause: Error) { - if (this.#options.reconnectStrategy === false) { - return false; - } else if (typeof this.#options.reconnectStrategy === 'number') { - return this.#options.reconnectStrategy; - } else if (this.#options.reconnectStrategy) { - try { - const retryIn = this.#options.reconnectStrategy(retries, cause); - if (retryIn !== false && !(retryIn instanceof Error) && typeof retryIn !== 'number') { - throw new TypeError(`Reconnect strategy should return \`false | Error | number\`, got ${retryIn} instead`); - } - - return retryIn; - } catch (err) { - this.emit('error', err); - } - } + constructor(initiator: RedisSocketInitiator, options?: RedisSocketOptions) { + super(); - return Math.min(retries * 50, 500); - } + this.#initiator = initiator; + this.#options = RedisSocket.#initiateOptions(options); + } - #shouldReconnect(retries: number, cause: Error) { - const retryIn = this.#reconnectStrategy(retries, cause); - if (retryIn === false) { - this.#isOpen = false; - this.emit('error', cause); - return cause; - } else if (retryIn instanceof Error) { - this.#isOpen = false; - this.emit('error', cause); - return new ReconnectStrategyError(retryIn, cause); + #reconnectStrategy(retries: number, cause: Error) { + if (this.#options.reconnectStrategy === false) { + return false; + } else if (typeof this.#options.reconnectStrategy === 'number') { + return this.#options.reconnectStrategy; + } else if (this.#options.reconnectStrategy) { + try { + const retryIn = this.#options.reconnectStrategy(retries, cause); + if (retryIn !== false && !(retryIn instanceof Error) && typeof retryIn !== 'number') { + throw new TypeError(`Reconnect strategy should return \`false | Error | number\`, got ${retryIn} instead`); } return retryIn; + } catch (err) { + this.emit('error', err); + } } - async connect(): Promise { - if (this.#isOpen) { - throw new Error('Socket already opened'); - } - - this.#isOpen = true; - return this.#connect(); - } - - async #connect(): Promise { - let retries = 0; - do { - try { - this.#socket = await this.#createSocket(); - this.#writableNeedDrain = false; - this.emit('connect'); - - try { - await this.#initiator(); - } catch (err) { - this.#socket.destroy(); - this.#socket = undefined; - throw err; - } - this.#isReady = true; - this.emit('ready'); - } catch (err) { - const retryIn = this.#shouldReconnect(retries++, err as Error); - if (typeof retryIn !== 'number') { - throw retryIn; - } - - this.emit('error', err); - await promiseTimeout(retryIn); - this.emit('reconnecting'); - } - } while (this.#isOpen && !this.#isReady); + return Math.min(retries * 50, 500); + } + + #shouldReconnect(retries: number, cause: Error) { + const retryIn = this.#reconnectStrategy(retries, cause); + if (retryIn === false) { + this.#isOpen = false; + this.emit('error', cause); + return cause; + } else if (retryIn instanceof Error) { + this.#isOpen = false; + this.emit('error', cause); + return new ReconnectStrategyError(retryIn, cause); } - #createSocket(): Promise { - return new Promise((resolve, reject) => { - const { connectEvent, socket } = RedisSocket.#isTlsSocket(this.#options) ? - this.#createTlsSocket() : - this.#createNetSocket(); - - if (this.#options.connectTimeout) { - socket.setTimeout(this.#options.connectTimeout, () => socket.destroy(new ConnectionTimeoutError())); - } - - if (this.#isSocketUnrefed) { - socket.unref(); - } - - socket - .setNoDelay(this.#options.noDelay) - .once('error', reject) - .once(connectEvent, () => { - socket - .setTimeout(0) - // https://github.com/nodejs/node/issues/31663 - .setKeepAlive(this.#options.keepAlive !== false, this.#options.keepAlive || 0) - .off('error', reject) - .once('error', (err: Error) => this.#onSocketError(err)) - .once('close', hadError => { - if (!hadError && this.#isReady && this.#socket === socket) { - this.#onSocketError(new SocketClosedUnexpectedlyError()); - } - }) - .on('drain', () => { - this.#writableNeedDrain = false; - this.emit('drain'); - }) - .on('data', data => this.emit('data', data)); - - resolve(socket); - }); - }); - } + return retryIn; + } - #createNetSocket(): CreateSocketReturn { - return { - connectEvent: 'connect', - socket: net.connect(this.#options as net.NetConnectOpts) // TODO - }; + async connect(): Promise { + if (this.#isOpen) { + throw new Error('Socket already opened'); } - #createTlsSocket(): CreateSocketReturn { - return { - connectEvent: 'secureConnect', - socket: tls.connect(this.#options as tls.ConnectionOptions) // TODO - }; - } + this.#isOpen = true; + return this.#connect(); + } + + async #connect(): Promise { + let retries = 0; + do { + try { + this.#socket = await this.#createSocket(); + this.#writableNeedDrain = false; + this.emit('connect'); + + try { + await this.#initiator(); + } catch (err) { + this.#socket.destroy(); + this.#socket = undefined; + throw err; + } + this.#isReady = true; + this.emit('ready'); + } catch (err) { + const retryIn = this.#shouldReconnect(retries++, err as Error); + if (typeof retryIn !== 'number') { + throw retryIn; + } - #onSocketError(err: Error): void { - this.#isReady = false; this.emit('error', err); - - if (!this.#isOpen || typeof this.#shouldReconnect(0, err) !== 'number') return; - + await promiseTimeout(retryIn); this.emit('reconnecting'); - this.#connect().catch(() => { - // the error was already emitted, silently ignore it + } + } while (this.#isOpen && !this.#isReady); + } + + #createSocket(): Promise { + return new Promise((resolve, reject) => { + const { connectEvent, socket } = RedisSocket.#isTlsSocket(this.#options) ? + this.#createTlsSocket() : + this.#createNetSocket(); + + if (this.#options.connectTimeout) { + socket.setTimeout(this.#options.connectTimeout, () => socket.destroy(new ConnectionTimeoutError())); + } + + if (this.#isSocketUnrefed) { + socket.unref(); + } + + socket + .setNoDelay(this.#options.noDelay) + .once('error', reject) + .once(connectEvent, () => { + socket + .setTimeout(0) + // https://github.com/nodejs/node/issues/31663 + .setKeepAlive(this.#options.keepAlive !== false, this.#options.keepAlive || 0) + .off('error', reject) + .once('error', (err: Error) => this.#onSocketError(err)) + .once('close', hadError => { + if (!hadError && this.#isOpen && this.#socket === socket) { + this.#onSocketError(new SocketClosedUnexpectedlyError()); + } + }) + .on('drain', () => { + this.#writableNeedDrain = false; + this.emit('drain'); + }) + .on('data', data => this.emit('data', data)); + + resolve(socket); }); + }); + } + + #createNetSocket(): CreateSocketReturn { + return { + connectEvent: 'connect', + socket: net.connect(this.#options as net.NetConnectOpts) // TODO + }; + } + + #createTlsSocket(): CreateSocketReturn { + return { + connectEvent: 'secureConnect', + socket: tls.connect(this.#options as tls.ConnectionOptions) // TODO + }; + } + + #onSocketError(err: Error): void { + this.#isReady = false; + this.emit('error', err); + + if (!this.#isOpen || typeof this.#shouldReconnect(0, err) !== 'number') return; + + this.emit('reconnecting'); + this.#connect().catch(() => { + // the error was already emitted, silently ignore it + }); + } + + writeCommand(args: Array): void { + if (!this.#socket) { + throw new ClientClosedError(); } - writeCommand(args: RedisCommandArguments): void { - if (!this.#socket) { - throw new ClientClosedError(); - } - - for (const toWrite of args) { - this.#writableNeedDrain = !this.#socket.write(toWrite); - } + for (const toWrite of args) { + this.#writableNeedDrain = !this.#socket.write(toWrite); } + } - disconnect(): void { - if (!this.#isOpen) { - throw new ClientClosedError(); - } - - this.#isOpen = false; - this.#disconnect(); + disconnect(): void { + if (!this.#isOpen) { + throw new ClientClosedError(); } - #disconnect(): void { - this.#isReady = false; + this.#isOpen = false; + this.#disconnect(); + } - if (this.#socket) { - this.#socket.destroy(); - this.#socket = undefined; - } - - this.emit('end'); + #disconnect(): void { + this.#isReady = false; + + if (this.#socket) { + this.#socket.destroy(); + this.#socket = undefined; } - async quit(fn: () => Promise): Promise { - if (!this.#isOpen) { - throw new ClientClosedError(); - } + this.emit('end'); + } - this.#isOpen = false; - const reply = await fn(); - this.#disconnect(); - return reply; + async quit(fn: () => Promise): Promise { + if (!this.#isOpen) { + throw new ClientClosedError(); } - #isCorked = false; + this.#isOpen = false; + const reply = await fn(); + this.#disconnect(); + return reply; + } - cork(): void { - if (!this.#socket || this.#isCorked) { - return; - } + #isCorked = false; - this.#socket.cork(); - this.#isCorked = true; - - queueMicrotask(() => { - this.#socket?.uncork(); - this.#isCorked = false; - }); + cork(): void { + if (!this.#socket || this.#isCorked) { + return; } - ref(): void { - this.#isSocketUnrefed = false; - this.#socket?.ref(); - } + this.#socket.cork(); + this.#isCorked = true; - unref(): void { - this.#isSocketUnrefed = true; - this.#socket?.unref(); - } + queueMicrotask(() => { + this.#socket?.uncork(); + this.#isCorked = false; + }); + } + + ref(): void { + this.#isSocketUnrefed = false; + this.#socket?.ref(); + } + + unref(): void { + this.#isSocketUnrefed = true; + this.#socket?.unref(); + } } diff --git a/packages/client/lib/cluster/cluster-slots.ts b/packages/client/lib/cluster/cluster-slots.ts index b540c2fa85f..e94ad52002d 100644 --- a/packages/client/lib/cluster/cluster-slots.ts +++ b/packages/client/lib/cluster/cluster-slots.ts @@ -1,621 +1,618 @@ -import RedisClient, { InstantiableRedisClient, RedisClientType } from '../client'; import { RedisClusterClientOptions, RedisClusterOptions } from '.'; -import { RedisCommandArgument, RedisFunctions, RedisModules, RedisScripts } from '../commands'; import { RootNodesUnavailableError } from '../errors'; -import { ClusterSlotsNode } from '../commands/CLUSTER_SLOTS'; +import RedisClient, { RedisClientOptions, RedisClientType } from '../client'; import { types } from 'util'; -import { ChannelListeners, PubSubType, PubSubTypeListeners } from '../client/pub-sub'; import { EventEmitter } from 'stream'; +import { ChannelListeners, PubSubType, PubSubTypeListeners } from '../client/pub-sub'; +import { RedisArgument, RedisFunctions, RedisModules, RedisScripts, RespVersions } from '../RESP/types'; +// TODO: ?! // We need to use 'require', because it's not possible with Typescript to import // function that are exported as 'module.exports = function`, without esModuleInterop // set to true. const calculateSlot = require('cluster-key-slot'); interface NodeAddress { - host: string; - port: number; + host: string; + port: number; } export type NodeAddressMap = { - [address: string]: NodeAddress; + [address: string]: NodeAddress; } | ((address: string) => NodeAddress | undefined); type ValueOrPromise = T | Promise; type ClientOrPromise< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = ValueOrPromise>; + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions = 2 +> = ValueOrPromise>; export interface Node< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions > { - address: string; - client?: ClientOrPromise; + address: string; + client?: ClientOrPromise; } export interface ShardNode< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> extends Node { - id: string; - host: string; - port: number; - readonly: boolean; + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> extends Node { + host: string; + port: number; + readonly: boolean; } export interface MasterNode< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> extends ShardNode { - pubSubClient?: ClientOrPromise; + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> extends ShardNode { + pubSubClient?: ClientOrPromise; } export interface Shard< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions > { - master: MasterNode; - replicas?: Array>; - nodesIterator?: IterableIterator>; + master: MasterNode; + replicas?: Array>; + nodesIterator?: IterableIterator>; } type ShardWithReplicas< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = Shard & Required, 'replicas'>>; + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> = Shard & Required, 'replicas'>>; export type PubSubNode< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = Required>; + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> = Required>; type PubSubToResubscribe = Record< - PubSubType.CHANNELS | PubSubType.PATTERNS, - PubSubTypeListeners + PubSubType.CHANNELS | PubSubType.PATTERNS, + PubSubTypeListeners >; export type OnShardedChannelMovedError = ( - err: unknown, - channel: string, - listeners?: ChannelListeners + err: unknown, + channel: string, + listeners?: ChannelListeners ) => void; export default class RedisClusterSlots< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions > { - static #SLOTS = 16384; - - readonly #options: RedisClusterOptions; - readonly #Client: InstantiableRedisClient; - readonly #emit: EventEmitter['emit']; - slots = new Array>(RedisClusterSlots.#SLOTS); - shards = new Array>(); - masters = new Array>(); - replicas = new Array>(); - readonly nodeByAddress = new Map | ShardNode>(); - pubSubNode?: PubSubNode; - - #isOpen = false; - - get isOpen() { - return this.#isOpen; - } - - constructor( - options: RedisClusterOptions, - emit: EventEmitter['emit'] - ) { - this.#options = options; - this.#Client = RedisClient.extend(options); - this.#emit = emit; - } - - async connect() { - if (this.#isOpen) { - throw new Error('Cluster already open'); - } - - this.#isOpen = true; - try { - await this.#discoverWithRootNodes(); - } catch (err) { - this.#isOpen = false; - throw err; - } + private static _SLOTS = 16384; + + private readonly _options: RedisClusterOptions; + private readonly _clientFactory: ReturnType>; + private readonly _emit: EventEmitter['emit']; + slots = new Array>(RedisClusterSlots._SLOTS); + shards = new Array>(); + masters = new Array>(); + replicas = new Array>(); + readonly nodeByAddress = new Map | ShardNode>(); + pubSubNode?: PubSubNode; + + private _isOpen = false; + + get isOpen() { + return this._isOpen; + } + + constructor( + options: RedisClusterOptions, + emit: EventEmitter['emit'] + ) { + this._options = options; + this._clientFactory = RedisClient.factory(options); + this._emit = emit; + } + + async connect() { + if (this._isOpen) { + throw new Error('Cluster already open'); } - async #discoverWithRootNodes() { - let start = Math.floor(Math.random() * this.#options.rootNodes.length); - for (let i = start; i < this.#options.rootNodes.length; i++) { - if (await this.#discover(this.#options.rootNodes[i])) return; - } - - for (let i = 0; i < start; i++) { - if (await this.#discover(this.#options.rootNodes[i])) return; - } - - throw new RootNodesUnavailableError(); + this._isOpen = true; + try { + await this._discoverWithRootNodes(); + } catch (err) { + this._isOpen = false; + throw err; } + } - #resetSlots() { - this.slots = new Array(RedisClusterSlots.#SLOTS); - this.shards = []; - this.masters = []; - this.replicas = []; - this.#randomNodeIterator = undefined; + private async _discoverWithRootNodes() { + let start = Math.floor(Math.random() * this._options.rootNodes.length); + for (let i = start; i < this._options.rootNodes.length; i++) { + if (await this._discover(this._options.rootNodes[i])) return; } - async #discover(rootNode?: RedisClusterClientOptions) { - this.#resetSlots(); - const addressesInUse = new Set(); - - try { - const shards = await this.#getShards(rootNode), - promises: Array> = [], - eagerConnect = this.#options.minimizeConnections !== true; - for (const { from, to, master, replicas } of shards) { - const shard: Shard = { - master: this.#initiateSlotNode(master, false, eagerConnect, addressesInUse, promises) - }; - - if (this.#options.useReplicas) { - shard.replicas = replicas.map(replica => - this.#initiateSlotNode(replica, true, eagerConnect, addressesInUse, promises) - ); - } - - this.shards.push(shard); - - for (let i = from; i <= to; i++) { - this.slots[i] = shard; - } - } - - if (this.pubSubNode && !addressesInUse.has(this.pubSubNode.address)) { - if (types.isPromise(this.pubSubNode.client)) { - promises.push( - this.pubSubNode.client.then(client => client.disconnect()) - ); - this.pubSubNode = undefined; - } else { - promises.push(this.pubSubNode.client.disconnect()); - - const channelsListeners = this.pubSubNode.client.getPubSubListeners(PubSubType.CHANNELS), - patternsListeners = this.pubSubNode.client.getPubSubListeners(PubSubType.PATTERNS); - - if (channelsListeners.size || patternsListeners.size) { - promises.push( - this.#initiatePubSubClient({ - [PubSubType.CHANNELS]: channelsListeners, - [PubSubType.PATTERNS]: patternsListeners - }) - ); - } - } - } - - for (const [address, node] of this.nodeByAddress.entries()) { - if (addressesInUse.has(address)) continue; - - if (node.client) { - promises.push( - this.#execOnNodeClient(node.client, client => client.disconnect()) - ); - } - - const { pubSubClient } = node as MasterNode; - if (pubSubClient) { - promises.push( - this.#execOnNodeClient(pubSubClient, client => client.disconnect()) - ); - } - - this.nodeByAddress.delete(address); - } - - await Promise.all(promises); - - return true; - } catch (err) { - this.#emit('error', err); - return false; - } + for (let i = 0; i < start; i++) { + if (await this._discover(this._options.rootNodes[i])) return; } - async #getShards(rootNode?: RedisClusterClientOptions) { - const client = new this.#Client( - this.#clientOptionsDefaults(rootNode, true) - ); - - client.on('error', err => this.#emit('error', err)); - - await client.connect(); + throw new RootNodesUnavailableError(); + } + + private _resetSlots() { + this.slots = new Array(RedisClusterSlots._SLOTS); + this.shards = []; + this.masters = []; + this.replicas = []; + this._randomNodeIterator = undefined; + } + + private async _discover(rootNode: RedisClusterClientOptions) { + this._resetSlots(); + const addressesInUse = new Set(); + + try { + const shards = await this._getShards(rootNode), + promises: Array> = [], + eagerConnect = this._options.minimizeConnections !== true; + + type a = typeof shards; + for (const { from, to, master, replicas } of shards) { + const shard: Shard = { + master: this._initiateSlotNode(master, false, eagerConnect, addressesInUse, promises) + }; - try { - // using `CLUSTER SLOTS` and not `CLUSTER SHARDS` to support older versions - return await client.clusterSlots(); - } finally { - await client.disconnect(); + if (this._options.useReplicas) { + shard.replicas = replicas.map(replica => + this._initiateSlotNode(replica, true, eagerConnect, addressesInUse, promises) + ); } - } - #getNodeAddress(address: string): NodeAddress | undefined { - switch (typeof this.#options.nodeAddressMap) { - case 'object': - return this.#options.nodeAddressMap[address]; + this.shards.push(shard); - case 'function': - return this.#options.nodeAddressMap(address); + for (let i = from; i <= to; i++) { + this.slots[i] = shard; } - } - - #clientOptionsDefaults( - options?: RedisClusterClientOptions, - disableReconnect?: boolean - ): RedisClusterClientOptions | undefined { - let result: RedisClusterClientOptions | undefined; - if (this.#options.defaults) { - let socket; - if (this.#options.defaults.socket) { - socket = options?.socket ? { - ...this.#options.defaults.socket, - ...options.socket - } : this.#options.defaults.socket; - } else { - socket = options?.socket; - } - - result = { - ...this.#options.defaults, - ...options, - socket - }; + } + + if (this.pubSubNode && !addressesInUse.has(this.pubSubNode.address)) { + if (types.isPromise(this.pubSubNode.client)) { + promises.push( + this.pubSubNode.client.then(client => client.disconnect()) + ); + this.pubSubNode = undefined; } else { - result = options; - } - - if (disableReconnect) { - result ??= {}; - result.socket ??= {}; - result.socket.reconnectStrategy = false; + promises.push(this.pubSubNode.client.disconnect()); + + const channelsListeners = this.pubSubNode.client.getPubSubListeners(PubSubType.CHANNELS), + patternsListeners = this.pubSubNode.client.getPubSubListeners(PubSubType.PATTERNS); + + if (channelsListeners.size || patternsListeners.size) { + promises.push( + this._initiatePubSubClient({ + [PubSubType.CHANNELS]: channelsListeners, + [PubSubType.PATTERNS]: patternsListeners + }) + ); + } } + } - return result; - } + for (const [address, node] of this.nodeByAddress.entries()) { + if (addressesInUse.has(address)) continue; - #initiateSlotNode( - { id, ip, port }: ClusterSlotsNode, - readonly: boolean, - eagerConnent: boolean, - addressesInUse: Set, - promises: Array> - ) { - const address = `${ip}:${port}`; - addressesInUse.add(address); - - let node = this.nodeByAddress.get(address); - if (!node) { - node = { - id, - host: ip, - port, - address, - readonly, - client: undefined - }; - - if (eagerConnent) { - promises.push(this.#createNodeClient(node)); - } - - this.nodeByAddress.set(address, node); + if (node.client) { + promises.push( + this._execOnNodeClient(node.client, client => client.disconnect()) + ); } - (readonly ? this.replicas : this.masters).push(node); - - return node; - } + const { pubSubClient } = node as MasterNode; + if (pubSubClient) { + promises.push( + this._execOnNodeClient(pubSubClient, client => client.disconnect()) + ); + } - async #createClient( - node: ShardNode, - readonly = node.readonly - ) { - const client = new this.#Client( - this.#clientOptionsDefaults({ - socket: this.#getNodeAddress(node.address) ?? { - host: node.host, - port: node.port - }, - readonly - }) - ); - client.on('error', err => this.#emit('error', err)); + this.nodeByAddress.delete(address); + } - await client.connect(); + await Promise.all(promises); - return client; + return true; + } catch (err) { + this._emit('error', err); + return false; } + } - #createNodeClient(node: ShardNode) { - const promise = this.#createClient(node) - .then(client => { - node.client = client; - return client; - }) - .catch(err => { - node.client = undefined; - throw err; - }); - node.client = promise; - return promise; - } + private async _getShards(rootNode: RedisClusterClientOptions) { + const options = this._clientOptionsDefaults(rootNode)!; + options.socket ??= {}; + options.socket.reconnectStrategy = false; + options.RESP = this._options.RESP; - nodeClient(node: ShardNode) { - return node.client ?? this.#createNodeClient(node); - } + const client = RedisClient.factory(this._options)(options); + + client.on('error', err => this._emit('error', err)); - #runningRediscoverPromise?: Promise; + await client.connect(); - async rediscover(startWith: RedisClientType): Promise { - this.#runningRediscoverPromise ??= this.#rediscover(startWith) - .finally(() => this.#runningRediscoverPromise = undefined); - return this.#runningRediscoverPromise; + try { + // switch to `CLUSTER SHARDS` when Redis 7.0 will be the minimum supported version + return await client.clusterSlots(); + } finally { + await client.disconnect(); } + } - async #rediscover(startWith: RedisClientType): Promise { - if (await this.#discover(startWith.options)) return; + private _getNodeAddress(address: string): NodeAddress | undefined { + switch (typeof this._options.nodeAddressMap) { + case 'object': + return this._options.nodeAddressMap[address]; - return this.#discoverWithRootNodes(); + case 'function': + return this._options.nodeAddressMap(address); } - - quit(): Promise { - return this.#destroy(client => client.quit()); + } + + private _clientOptionsDefaults(options?: RedisClientOptions): RedisClientOptions | undefined { + if (!this._options.defaults) return options; + + let socket; + if (this._options.defaults.socket) { + socket = options?.socket ? { + ...this._options.defaults.socket, + ...options.socket + } : this._options.defaults.socket; + } else { + socket = options?.socket; } - disconnect(): Promise { - return this.#destroy(client => client.disconnect()); + return { + ...this._options.defaults, + ...options, + socket + }; + } + + private _initiateSlotNode( + slotAddress: NodeAddress, + readonly: boolean, + eagerConnent: boolean, + addressesInUse: Set, + promises: Array> + ) { + const address = `${slotAddress.host}:${slotAddress.port}`; + addressesInUse.add(address); + + let node = this.nodeByAddress.get(address); + if (!node) { + node = { + ...slotAddress, + address, + readonly, + client: undefined + }; + + if (eagerConnent) { + promises.push(this._createNodeClient(node)); + } + + this.nodeByAddress.set(address, node); } - async #destroy(fn: (client: RedisClientType) => Promise): Promise { - this.#isOpen = false; - - const promises = []; - for (const { master, replicas } of this.shards) { - if (master.client) { - promises.push( - this.#execOnNodeClient(master.client, fn) - ); - } - - if (master.pubSubClient) { - promises.push( - this.#execOnNodeClient(master.pubSubClient, fn) - ); - } - - if (replicas) { - for (const { client } of replicas) { - if (client) { - promises.push( - this.#execOnNodeClient(client, fn) - ); - } - } - } - } + (readonly ? this.replicas : this.masters).push(node); + + return node; + } + + private async _createClient( + node: ShardNode, + readonly = node.readonly + ) { + const client = this._clientFactory( + this._clientOptionsDefaults({ + socket: this._getNodeAddress(node.address) ?? { + host: node.host, + port: node.port + }, + readonly, + RESP: this._options.RESP + }) + ); + client.on('error', err => this._emit('error', err)); + + await client.connect(); + + return client; + } + + private _createNodeClient(node: ShardNode) { + const promise = this._createClient(node) + .then(client => { + node.client = client; + return client; + }) + .catch(err => { + node.client = undefined; + throw err; + }); + node.client = promise; + return promise; + } + + nodeClient(node: ShardNode) { + return node.client ?? this._createNodeClient(node); + } + + #runningRediscoverPromise?: Promise; + + async rediscover(startWith: RedisClientType): Promise { + this.#runningRediscoverPromise ??= this._rediscover(startWith) + .finally(() => this.#runningRediscoverPromise = undefined); + return this.#runningRediscoverPromise; + } + + private async _rediscover(startWith: RedisClientType): Promise { + if (await this._discover(startWith.options!)) return; + + return this._discoverWithRootNodes(); + } + + quit(): Promise { + return this._destroy(client => client.quit()); + } + + disconnect(): Promise { + return this._destroy(client => client.disconnect()); + } + + private async _destroy(fn: (client: RedisClientType) => Promise): Promise { + this._isOpen = false; + + const promises = []; + for (const { master, replicas } of this.shards) { + if (master.client) { + promises.push( + this._execOnNodeClient(master.client, fn) + ); + } - if (this.pubSubNode) { - promises.push(this.#execOnNodeClient(this.pubSubNode.client, fn)); - this.pubSubNode = undefined; + if (master.pubSubClient) { + promises.push( + this._execOnNodeClient(master.pubSubClient, fn) + ); + } + + if (replicas) { + for (const { client } of replicas) { + if (client) { + promises.push( + this._execOnNodeClient(client, fn) + ); + } } - - this.#resetSlots(); - this.nodeByAddress.clear(); - - await Promise.allSettled(promises); + } } - #execOnNodeClient( - client: ClientOrPromise, - fn: (client: RedisClientType) => Promise - ) { - return types.isPromise(client) ? - client.then(fn) : - fn(client); + if (this.pubSubNode) { + promises.push(this._execOnNodeClient(this.pubSubNode.client, fn)); + this.pubSubNode = undefined; } - getClient( - firstKey: RedisCommandArgument | undefined, - isReadonly: boolean | undefined - ): ClientOrPromise { - if (!firstKey) { - return this.nodeClient(this.getRandomNode()); - } - - const slotNumber = calculateSlot(firstKey); - if (!isReadonly) { - return this.nodeClient(this.slots[slotNumber].master); - } - - return this.nodeClient(this.getSlotRandomNode(slotNumber)); + this._resetSlots(); + this.nodeByAddress.clear(); + + await Promise.allSettled(promises); + } + + private _execOnNodeClient( + client: ClientOrPromise, + fn: (client: RedisClientType) => Promise + ) { + return types.isPromise(client) ? + client.then(fn) : + fn(client); + } + + getClient( + firstKey: RedisArgument | undefined, + isReadonly: boolean | undefined + ): ClientOrPromise { + if (!firstKey) { + return this.nodeClient(this.getRandomNode()); } - *#iterateAllNodes() { - let i = Math.floor(Math.random() * (this.masters.length + this.replicas.length)); - if (i < this.masters.length) { - do { - yield this.masters[i]; - } while (++i < this.masters.length); - - for (const replica of this.replicas) { - yield replica; - } - } else { - i -= this.masters.length; - do { - yield this.replicas[i]; - } while (++i < this.replicas.length); - } - - while (true) { - for (const master of this.masters) { - yield master; - } - - for (const replica of this.replicas) { - yield replica; - } - } + const slotNumber = calculateSlot(firstKey); + if (!isReadonly) { + return this.nodeClient(this.slots[slotNumber].master); } - #randomNodeIterator?: IterableIterator>; - - getRandomNode() { - this.#randomNodeIterator ??= this.#iterateAllNodes(); - return this.#randomNodeIterator.next().value as ShardNode; + return this.nodeClient(this.getSlotRandomNode(slotNumber)); + } + + private *_iterateAllNodes() { + let i = Math.floor(Math.random() * (this.masters.length + this.replicas.length)); + if (i < this.masters.length) { + do { + yield this.masters[i]; + } while (++i < this.masters.length); + + for (const replica of this.replicas) { + yield replica; + } + } else { + i -= this.masters.length; + do { + yield this.replicas[i]; + } while (++i < this.replicas.length); } - *#slotNodesIterator(slot: ShardWithReplicas) { - let i = Math.floor(Math.random() * (1 + slot.replicas.length)); - if (i < slot.replicas.length) { - do { - yield slot.replicas[i]; - } while (++i < slot.replicas.length); - } + while (true) { + for (const master of this.masters) { + yield master; + } - while (true) { - yield slot.master; - - for (const replica of slot.replicas) { - yield replica; - } - } + for (const replica of this.replicas) { + yield replica; + } } + } - getSlotRandomNode(slotNumber: number) { - const slot = this.slots[slotNumber]; - if (!slot.replicas?.length) { - return slot.master; - } - - slot.nodesIterator ??= this.#slotNodesIterator(slot as ShardWithReplicas); - return slot.nodesIterator.next().value as ShardNode; - } + _randomNodeIterator?: IterableIterator>; - getMasterByAddress(address: string) { - const master = this.nodeByAddress.get(address); - if (!master) return; + getRandomNode() { + this._randomNodeIterator ??= this._iterateAllNodes(); + return this._randomNodeIterator.next().value as ShardNode; + } - return this.nodeClient(master); + private *_slotNodesIterator(slot: ShardWithReplicas) { + let i = Math.floor(Math.random() * (1 + slot.replicas.length)); + if (i < slot.replicas.length) { + do { + yield slot.replicas[i]; + } while (++i < slot.replicas.length); } - getPubSubClient() { - return this.pubSubNode ? - this.pubSubNode.client : - this.#initiatePubSubClient(); - } + while (true) { + yield slot.master; - async #initiatePubSubClient(toResubscribe?: PubSubToResubscribe) { - const index = Math.floor(Math.random() * (this.masters.length + this.replicas.length)), - node = index < this.masters.length ? - this.masters[index] : - this.replicas[index - this.masters.length]; - - this.pubSubNode = { - address: node.address, - client: this.#createClient(node, true) - .then(async client => { - if (toResubscribe) { - await Promise.all([ - client.extendPubSubListeners(PubSubType.CHANNELS, toResubscribe[PubSubType.CHANNELS]), - client.extendPubSubListeners(PubSubType.PATTERNS, toResubscribe[PubSubType.PATTERNS]) - ]); - } - - this.pubSubNode!.client = client; - return client; - }) - .catch(err => { - this.pubSubNode = undefined; - throw err; - }) - }; - - return this.pubSubNode.client as Promise>; + for (const replica of slot.replicas) { + yield replica; + } } + } - async executeUnsubscribeCommand( - unsubscribe: (client: RedisClientType) => Promise - ): Promise { - const client = await this.getPubSubClient(); - await unsubscribe(client); - - if (!client.isPubSubActive) { - await client.disconnect(); - this.pubSubNode = undefined; - } + getSlotRandomNode(slotNumber: number) { + const slot = this.slots[slotNumber]; + if (!slot.replicas?.length) { + return slot.master; } - getShardedPubSubClient(channel: string) { - const { master } = this.slots[calculateSlot(channel)]; - return master.pubSubClient ?? this.#initiateShardedPubSubClient(master); + slot.nodesIterator ??= this._slotNodesIterator(slot as ShardWithReplicas); + return slot.nodesIterator.next().value as ShardNode; + } + + getMasterByAddress(address: string) { + const master = this.nodeByAddress.get(address); + if (!master) return; + + return this.nodeClient(master); + } + + getPubSubClient() { + return this.pubSubNode ? + this.pubSubNode.client : + this._initiatePubSubClient(); + } + + private async _initiatePubSubClient(toResubscribe?: PubSubToResubscribe) { + const index = Math.floor(Math.random() * (this.masters.length + this.replicas.length)), + node = index < this.masters.length ? + this.masters[index] : + this.replicas[index - this.masters.length]; + + this.pubSubNode = { + address: node.address, + client: this._createClient(node, true) + .then(async client => { + if (toResubscribe) { + await Promise.all([ + client.extendPubSubListeners(PubSubType.CHANNELS, toResubscribe[PubSubType.CHANNELS]), + client.extendPubSubListeners(PubSubType.PATTERNS, toResubscribe[PubSubType.PATTERNS]) + ]); + } + + this.pubSubNode!.client = client; + return client; + }) + .catch(err => { + this.pubSubNode = undefined; + throw err; + }) + }; + + return this.pubSubNode.client as Promise>; + } + + async executeUnsubscribeCommand( + unsubscribe: (client: RedisClientType) => Promise + ): Promise { + const client = await this.getPubSubClient(); + await unsubscribe(client); + + if (!client.isPubSubActive) { + await client.disconnect(); + this.pubSubNode = undefined; } + } + + getShardedPubSubClient(channel: string) { + const { master } = this.slots[calculateSlot(channel)]; + return master.pubSubClient ?? this.#initiateShardedPubSubClient(master); + } + + #initiateShardedPubSubClient(master: MasterNode) { + const promise = this._createClient(master, true) + .then(client => { + client.on('server-sunsubscribe', async (channel, listeners) => { + try { + await this.rediscover(client); + const redirectTo = await this.getShardedPubSubClient(channel); + redirectTo.extendPubSubChannelListeners( + PubSubType.SHARDED, + channel, + listeners + ); + } catch (err) { + this._emit('sharded-shannel-moved-error', err, channel, listeners); + } + }); + + master.pubSubClient = client; + return client; + }) + .catch(err => { + master.pubSubClient = undefined; + throw err; + }); - #initiateShardedPubSubClient(master: MasterNode) { - const promise = this.#createClient(master, true) - .then(client => { - client.on('server-sunsubscribe', async (channel, listeners) => { - try { - await this.rediscover(client); - const redirectTo = await this.getShardedPubSubClient(channel); - redirectTo.extendPubSubChannelListeners( - PubSubType.SHARDED, - channel, - listeners - ); - } catch (err) { - this.#emit('sharded-shannel-moved-error', err, channel, listeners); - } - }); - - master.pubSubClient = client; - return client; - }) - .catch(err => { - master.pubSubClient = undefined; - throw err; - }); - - master.pubSubClient = promise; - - return promise; - } + master.pubSubClient = promise; + + return promise; + } - async executeShardedUnsubscribeCommand( - channel: string, - unsubscribe: (client: RedisClientType) => Promise - ): Promise { - const { master } = this.slots[calculateSlot(channel)]; - if (!master.pubSubClient) return Promise.resolve(); + async executeShardedUnsubscribeCommand( + channel: string, + unsubscribe: (client: RedisClientType) => Promise + ): Promise { + const { master } = this.slots[calculateSlot(channel)]; + if (!master.pubSubClient) return Promise.resolve(); - const client = await master.pubSubClient; - await unsubscribe(client); + const client = await master.pubSubClient; + await unsubscribe(client); - if (!client.isPubSubActive) { - await client.disconnect(); - master.pubSubClient = undefined; - } + if (!client.isPubSubActive) { + await client.disconnect(); + master.pubSubClient = undefined; } + } } diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts deleted file mode 100644 index 58fa651be1b..00000000000 --- a/packages/client/lib/cluster/commands.ts +++ /dev/null @@ -1,637 +0,0 @@ - -import * as APPEND from '../commands/APPEND'; -import * as BITCOUNT from '../commands/BITCOUNT'; -import * as BITFIELD_RO from '../commands/BITFIELD_RO'; -import * as BITFIELD from '../commands/BITFIELD'; -import * as BITOP from '../commands/BITOP'; -import * as BITPOS from '../commands/BITPOS'; -import * as BLMOVE from '../commands/BLMOVE'; -import * as BLMPOP from '../commands/BLMPOP'; -import * as BLPOP from '../commands/BLPOP'; -import * as BRPOP from '../commands/BRPOP'; -import * as BRPOPLPUSH from '../commands/BRPOPLPUSH'; -import * as BZMPOP from '../commands/BZMPOP'; -import * as BZPOPMAX from '../commands/BZPOPMAX'; -import * as BZPOPMIN from '../commands/BZPOPMIN'; -import * as COPY from '../commands/COPY'; -import * as DECR from '../commands/DECR'; -import * as DECRBY from '../commands/DECRBY'; -import * as DEL from '../commands/DEL'; -import * as DUMP from '../commands/DUMP'; -import * as EVAL_RO from '../commands/EVAL_RO'; -import * as EVAL from '../commands/EVAL'; -import * as EVALSHA_RO from '../commands/EVALSHA_RO'; -import * as EVALSHA from '../commands/EVALSHA'; -import * as EXISTS from '../commands/EXISTS'; -import * as EXPIRE from '../commands/EXPIRE'; -import * as EXPIREAT from '../commands/EXPIREAT'; -import * as EXPIRETIME from '../commands/EXPIRETIME'; -import * as FCALL_RO from '../commands/FCALL_RO'; -import * as FCALL from '../commands/FCALL'; -import * as GEOADD from '../commands/GEOADD'; -import * as GEODIST from '../commands/GEODIST'; -import * as GEOHASH from '../commands/GEOHASH'; -import * as GEOPOS from '../commands/GEOPOS'; -import * as GEORADIUS_RO_WITH from '../commands/GEORADIUS_RO_WITH'; -import * as GEORADIUS_RO from '../commands/GEORADIUS_RO'; -import * as GEORADIUS_WITH from '../commands/GEORADIUS_WITH'; -import * as GEORADIUS from '../commands/GEORADIUS'; -import * as GEORADIUSBYMEMBER_RO_WITH from '../commands/GEORADIUSBYMEMBER_RO_WITH'; -import * as GEORADIUSBYMEMBER_RO from '../commands/GEORADIUSBYMEMBER_RO'; -import * as GEORADIUSBYMEMBER_WITH from '../commands/GEORADIUSBYMEMBER_WITH'; -import * as GEORADIUSBYMEMBER from '../commands/GEORADIUSBYMEMBER'; -import * as GEORADIUSBYMEMBERSTORE from '../commands/GEORADIUSBYMEMBERSTORE'; -import * as GEORADIUSSTORE from '../commands/GEORADIUSSTORE'; -import * as GEOSEARCH_WITH from '../commands/GEOSEARCH_WITH'; -import * as GEOSEARCH from '../commands/GEOSEARCH'; -import * as GEOSEARCHSTORE from '../commands/GEOSEARCHSTORE'; -import * as GET from '../commands/GET'; -import * as GETBIT from '../commands/GETBIT'; -import * as GETDEL from '../commands/GETDEL'; -import * as GETEX from '../commands/GETEX'; -import * as GETRANGE from '../commands/GETRANGE'; -import * as GETSET from '../commands/GETSET'; -import * as HDEL from '../commands/HDEL'; -import * as HEXISTS from '../commands/HEXISTS'; -import * as HGET from '../commands/HGET'; -import * as HGETALL from '../commands/HGETALL'; -import * as HINCRBY from '../commands/HINCRBY'; -import * as HINCRBYFLOAT from '../commands/HINCRBYFLOAT'; -import * as HKEYS from '../commands/HKEYS'; -import * as HLEN from '../commands/HLEN'; -import * as HMGET from '../commands/HMGET'; -import * as HRANDFIELD_COUNT_WITHVALUES from '../commands/HRANDFIELD_COUNT_WITHVALUES'; -import * as HRANDFIELD_COUNT from '../commands/HRANDFIELD_COUNT'; -import * as HRANDFIELD from '../commands/HRANDFIELD'; -import * as HSCAN from '../commands/HSCAN'; -import * as HSET from '../commands/HSET'; -import * as HSETNX from '../commands/HSETNX'; -import * as HSTRLEN from '../commands/HSTRLEN'; -import * as HVALS from '../commands/HVALS'; -import * as INCR from '../commands/INCR'; -import * as INCRBY from '../commands/INCRBY'; -import * as INCRBYFLOAT from '../commands/INCRBYFLOAT'; -import * as LCS_IDX_WITHMATCHLEN from '../commands/LCS_IDX_WITHMATCHLEN'; -import * as LCS_IDX from '../commands/LCS_IDX'; -import * as LCS_LEN from '../commands/LCS_LEN'; -import * as LCS from '../commands/LCS'; -import * as LINDEX from '../commands/LINDEX'; -import * as LINSERT from '../commands/LINSERT'; -import * as LLEN from '../commands/LLEN'; -import * as LMOVE from '../commands/LMOVE'; -import * as LMPOP from '../commands/LMPOP'; -import * as LPOP_COUNT from '../commands/LPOP_COUNT'; -import * as LPOP from '../commands/LPOP'; -import * as LPOS_COUNT from '../commands/LPOS_COUNT'; -import * as LPOS from '../commands/LPOS'; -import * as LPUSH from '../commands/LPUSH'; -import * as LPUSHX from '../commands/LPUSHX'; -import * as LRANGE from '../commands/LRANGE'; -import * as LREM from '../commands/LREM'; -import * as LSET from '../commands/LSET'; -import * as LTRIM from '../commands/LTRIM'; -import * as MGET from '../commands/MGET'; -import * as MIGRATE from '../commands/MIGRATE'; -import * as MSET from '../commands/MSET'; -import * as MSETNX from '../commands/MSETNX'; -import * as OBJECT_ENCODING from '../commands/OBJECT_ENCODING'; -import * as OBJECT_FREQ from '../commands/OBJECT_FREQ'; -import * as OBJECT_IDLETIME from '../commands/OBJECT_IDLETIME'; -import * as OBJECT_REFCOUNT from '../commands/OBJECT_REFCOUNT'; -import * as PERSIST from '../commands/PERSIST'; -import * as PEXPIRE from '../commands/PEXPIRE'; -import * as PEXPIREAT from '../commands/PEXPIREAT'; -import * as PEXPIRETIME from '../commands/PEXPIRETIME'; -import * as PFADD from '../commands/PFADD'; -import * as PFCOUNT from '../commands/PFCOUNT'; -import * as PFMERGE from '../commands/PFMERGE'; -import * as PSETEX from '../commands/PSETEX'; -import * as PTTL from '../commands/PTTL'; -import * as PUBLISH from '../commands/PUBLISH'; -import * as RENAME from '../commands/RENAME'; -import * as RENAMENX from '../commands/RENAMENX'; -import * as RPOP_COUNT from '../commands/RPOP_COUNT'; -import * as RPOP from '../commands/RPOP'; -import * as RPOPLPUSH from '../commands/RPOPLPUSH'; -import * as RPUSH from '../commands/RPUSH'; -import * as RPUSHX from '../commands/RPUSHX'; -import * as SADD from '../commands/SADD'; -import * as SCARD from '../commands/SCARD'; -import * as SDIFF from '../commands/SDIFF'; -import * as SDIFFSTORE from '../commands/SDIFFSTORE'; -import * as SET from '../commands/SET'; -import * as SETBIT from '../commands/SETBIT'; -import * as SETEX from '../commands/SETEX'; -import * as SETNX from '../commands/SETNX'; -import * as SETRANGE from '../commands/SETRANGE'; -import * as SINTER from '../commands/SINTER'; -import * as SINTERCARD from '../commands/SINTERCARD'; -import * as SINTERSTORE from '../commands/SINTERSTORE'; -import * as SISMEMBER from '../commands/SISMEMBER'; -import * as SMEMBERS from '../commands/SMEMBERS'; -import * as SMISMEMBER from '../commands/SMISMEMBER'; -import * as SMOVE from '../commands/SMOVE'; -import * as SORT_RO from '../commands/SORT_RO'; -import * as SORT_STORE from '../commands/SORT_STORE'; -import * as SORT from '../commands/SORT'; -import * as SPOP from '../commands/SPOP'; -import * as SPUBLISH from '../commands/SPUBLISH'; -import * as SRANDMEMBER_COUNT from '../commands/SRANDMEMBER_COUNT'; -import * as SRANDMEMBER from '../commands/SRANDMEMBER'; -import * as SREM from '../commands/SREM'; -import * as SSCAN from '../commands/SSCAN'; -import * as STRLEN from '../commands/STRLEN'; -import * as SUNION from '../commands/SUNION'; -import * as SUNIONSTORE from '../commands/SUNIONSTORE'; -import * as TOUCH from '../commands/TOUCH'; -import * as TTL from '../commands/TTL'; -import * as TYPE from '../commands/TYPE'; -import * as UNLINK from '../commands/UNLINK'; -import * as WATCH from '../commands/WATCH'; -import * as XACK from '../commands/XACK'; -import * as XADD from '../commands/XADD'; -import * as XAUTOCLAIM_JUSTID from '../commands/XAUTOCLAIM_JUSTID'; -import * as XAUTOCLAIM from '../commands/XAUTOCLAIM'; -import * as XCLAIM_JUSTID from '../commands/XCLAIM_JUSTID'; -import * as XCLAIM from '../commands/XCLAIM'; -import * as XDEL from '../commands/XDEL'; -import * as XGROUP_CREATE from '../commands/XGROUP_CREATE'; -import * as XGROUP_CREATECONSUMER from '../commands/XGROUP_CREATECONSUMER'; -import * as XGROUP_DELCONSUMER from '../commands/XGROUP_DELCONSUMER'; -import * as XGROUP_DESTROY from '../commands/XGROUP_DESTROY'; -import * as XGROUP_SETID from '../commands/XGROUP_SETID'; -import * as XINFO_CONSUMERS from '../commands/XINFO_CONSUMERS'; -import * as XINFO_GROUPS from '../commands/XINFO_GROUPS'; -import * as XINFO_STREAM from '../commands/XINFO_STREAM'; -import * as XLEN from '../commands/XLEN'; -import * as XPENDING_RANGE from '../commands/XPENDING_RANGE'; -import * as XPENDING from '../commands/XPENDING'; -import * as XRANGE from '../commands/XRANGE'; -import * as XREAD from '../commands/XREAD'; -import * as XREADGROUP from '../commands/XREADGROUP'; -import * as XREVRANGE from '../commands/XREVRANGE'; -import * as XSETID from '../commands/XSETID'; -import * as XTRIM from '../commands/XTRIM'; -import * as ZADD from '../commands/ZADD'; -import * as ZCARD from '../commands/ZCARD'; -import * as ZCOUNT from '../commands/ZCOUNT'; -import * as ZDIFF_WITHSCORES from '../commands/ZDIFF_WITHSCORES'; -import * as ZDIFF from '../commands/ZDIFF'; -import * as ZDIFFSTORE from '../commands/ZDIFFSTORE'; -import * as ZINCRBY from '../commands/ZINCRBY'; -import * as ZINTER_WITHSCORES from '../commands/ZINTER_WITHSCORES'; -import * as ZINTER from '../commands/ZINTER'; -import * as ZINTERCARD from '../commands/ZINTERCARD'; -import * as ZINTERSTORE from '../commands/ZINTERSTORE'; -import * as ZLEXCOUNT from '../commands/ZLEXCOUNT'; -import * as ZMPOP from '../commands/ZMPOP'; -import * as ZMSCORE from '../commands/ZMSCORE'; -import * as ZPOPMAX_COUNT from '../commands/ZPOPMAX_COUNT'; -import * as ZPOPMAX from '../commands/ZPOPMAX'; -import * as ZPOPMIN_COUNT from '../commands/ZPOPMIN_COUNT'; -import * as ZPOPMIN from '../commands/ZPOPMIN'; -import * as ZRANDMEMBER_COUNT_WITHSCORES from '../commands/ZRANDMEMBER_COUNT_WITHSCORES'; -import * as ZRANDMEMBER_COUNT from '../commands/ZRANDMEMBER_COUNT'; -import * as ZRANDMEMBER from '../commands/ZRANDMEMBER'; -import * as ZRANGE_WITHSCORES from '../commands/ZRANGE_WITHSCORES'; -import * as ZRANGE from '../commands/ZRANGE'; -import * as ZRANGEBYLEX from '../commands/ZRANGEBYLEX'; -import * as ZRANGEBYSCORE_WITHSCORES from '../commands/ZRANGEBYSCORE_WITHSCORES'; -import * as ZRANGEBYSCORE from '../commands/ZRANGEBYSCORE'; -import * as ZRANGESTORE from '../commands/ZRANGESTORE'; -import * as ZRANK from '../commands/ZRANK'; -import * as ZREM from '../commands/ZREM'; -import * as ZREMRANGEBYLEX from '../commands/ZREMRANGEBYLEX'; -import * as ZREMRANGEBYRANK from '../commands/ZREMRANGEBYRANK'; -import * as ZREMRANGEBYSCORE from '../commands/ZREMRANGEBYSCORE'; -import * as ZREVRANK from '../commands/ZREVRANK'; -import * as ZSCAN from '../commands/ZSCAN'; -import * as ZSCORE from '../commands/ZSCORE'; -import * as ZUNION_WITHSCORES from '../commands/ZUNION_WITHSCORES'; -import * as ZUNION from '../commands/ZUNION'; -import * as ZUNIONSTORE from '../commands/ZUNIONSTORE'; - -export default { - APPEND, - append: APPEND, - BITCOUNT, - bitCount: BITCOUNT, - BITFIELD_RO, - bitFieldRo: BITFIELD_RO, - BITFIELD, - bitField: BITFIELD, - BITOP, - bitOp: BITOP, - BITPOS, - bitPos: BITPOS, - BLMOVE, - blMove: BLMOVE, - BLMPOP, - blmPop: BLMPOP, - BLPOP, - blPop: BLPOP, - BRPOP, - brPop: BRPOP, - BRPOPLPUSH, - brPopLPush: BRPOPLPUSH, - BZMPOP, - bzmPop: BZMPOP, - BZPOPMAX, - bzPopMax: BZPOPMAX, - BZPOPMIN, - bzPopMin: BZPOPMIN, - COPY, - copy: COPY, - DECR, - decr: DECR, - DECRBY, - decrBy: DECRBY, - DEL, - del: DEL, - DUMP, - dump: DUMP, - EVAL_RO, - evalRo: EVAL_RO, - EVAL, - eval: EVAL, - EVALSHA, - evalSha: EVALSHA, - EVALSHA_RO, - evalShaRo: EVALSHA_RO, - EXISTS, - exists: EXISTS, - EXPIRE, - expire: EXPIRE, - EXPIREAT, - expireAt: EXPIREAT, - EXPIRETIME, - expireTime: EXPIRETIME, - FCALL_RO, - fCallRo: FCALL_RO, - FCALL, - fCall: FCALL, - GEOADD, - geoAdd: GEOADD, - GEODIST, - geoDist: GEODIST, - GEOHASH, - geoHash: GEOHASH, - GEOPOS, - geoPos: GEOPOS, - GEORADIUS_RO_WITH, - geoRadiusRoWith: GEORADIUS_RO_WITH, - GEORADIUS_RO, - geoRadiusRo: GEORADIUS_RO, - GEORADIUS_WITH, - geoRadiusWith: GEORADIUS_WITH, - GEORADIUS, - geoRadius: GEORADIUS, - GEORADIUSBYMEMBER_RO_WITH, - geoRadiusByMemberRoWith: GEORADIUSBYMEMBER_RO_WITH, - GEORADIUSBYMEMBER_RO, - geoRadiusByMemberRo: GEORADIUSBYMEMBER_RO, - GEORADIUSBYMEMBER_WITH, - geoRadiusByMemberWith: GEORADIUSBYMEMBER_WITH, - GEORADIUSBYMEMBER, - geoRadiusByMember: GEORADIUSBYMEMBER, - GEORADIUSBYMEMBERSTORE, - geoRadiusByMemberStore: GEORADIUSBYMEMBERSTORE, - GEORADIUSSTORE, - geoRadiusStore: GEORADIUSSTORE, - GEOSEARCH_WITH, - geoSearchWith: GEOSEARCH_WITH, - GEOSEARCH, - geoSearch: GEOSEARCH, - GEOSEARCHSTORE, - geoSearchStore: GEOSEARCHSTORE, - GET, - get: GET, - GETBIT, - getBit: GETBIT, - GETDEL, - getDel: GETDEL, - GETEX, - getEx: GETEX, - GETRANGE, - getRange: GETRANGE, - GETSET, - getSet: GETSET, - HDEL, - hDel: HDEL, - HEXISTS, - hExists: HEXISTS, - HGET, - hGet: HGET, - HGETALL, - hGetAll: HGETALL, - HINCRBY, - hIncrBy: HINCRBY, - HINCRBYFLOAT, - hIncrByFloat: HINCRBYFLOAT, - HKEYS, - hKeys: HKEYS, - HLEN, - hLen: HLEN, - HMGET, - hmGet: HMGET, - HRANDFIELD_COUNT_WITHVALUES, - hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, - HRANDFIELD_COUNT, - hRandFieldCount: HRANDFIELD_COUNT, - HRANDFIELD, - hRandField: HRANDFIELD, - HSCAN, - hScan: HSCAN, - HSET, - hSet: HSET, - HSETNX, - hSetNX: HSETNX, - HSTRLEN, - hStrLen: HSTRLEN, - HVALS, - hVals: HVALS, - INCR, - incr: INCR, - INCRBY, - incrBy: INCRBY, - INCRBYFLOAT, - incrByFloat: INCRBYFLOAT, - LCS_IDX_WITHMATCHLEN, - lcsIdxWithMatchLen: LCS_IDX_WITHMATCHLEN, - LCS_IDX, - lcsIdx: LCS_IDX, - LCS_LEN, - lcsLen: LCS_LEN, - LCS, - lcs: LCS, - LINDEX, - lIndex: LINDEX, - LINSERT, - lInsert: LINSERT, - LLEN, - lLen: LLEN, - LMOVE, - lMove: LMOVE, - LMPOP, - lmPop: LMPOP, - LPOP_COUNT, - lPopCount: LPOP_COUNT, - LPOP, - lPop: LPOP, - LPOS_COUNT, - lPosCount: LPOS_COUNT, - LPOS, - lPos: LPOS, - LPUSH, - lPush: LPUSH, - LPUSHX, - lPushX: LPUSHX, - LRANGE, - lRange: LRANGE, - LREM, - lRem: LREM, - LSET, - lSet: LSET, - LTRIM, - lTrim: LTRIM, - MGET, - mGet: MGET, - MIGRATE, - migrate: MIGRATE, - MSET, - mSet: MSET, - MSETNX, - mSetNX: MSETNX, - OBJECT_ENCODING, - objectEncoding: OBJECT_ENCODING, - OBJECT_FREQ, - objectFreq: OBJECT_FREQ, - OBJECT_IDLETIME, - objectIdleTime: OBJECT_IDLETIME, - OBJECT_REFCOUNT, - objectRefCount: OBJECT_REFCOUNT, - PERSIST, - persist: PERSIST, - PEXPIRE, - pExpire: PEXPIRE, - PEXPIREAT, - pExpireAt: PEXPIREAT, - PEXPIRETIME, - pExpireTime: PEXPIRETIME, - PFADD, - pfAdd: PFADD, - PFCOUNT, - pfCount: PFCOUNT, - PFMERGE, - pfMerge: PFMERGE, - PSETEX, - pSetEx: PSETEX, - PTTL, - pTTL: PTTL, - PUBLISH, - publish: PUBLISH, - RENAME, - rename: RENAME, - RENAMENX, - renameNX: RENAMENX, - RPOP_COUNT, - rPopCount: RPOP_COUNT, - RPOP, - rPop: RPOP, - RPOPLPUSH, - rPopLPush: RPOPLPUSH, - RPUSH, - rPush: RPUSH, - RPUSHX, - rPushX: RPUSHX, - SADD, - sAdd: SADD, - SCARD, - sCard: SCARD, - SDIFF, - sDiff: SDIFF, - SDIFFSTORE, - sDiffStore: SDIFFSTORE, - SINTER, - sInter: SINTER, - SINTERCARD, - sInterCard: SINTERCARD, - SINTERSTORE, - sInterStore: SINTERSTORE, - SET, - set: SET, - SETBIT, - setBit: SETBIT, - SETEX, - setEx: SETEX, - SETNX, - setNX: SETNX, - SETRANGE, - setRange: SETRANGE, - SISMEMBER, - sIsMember: SISMEMBER, - SMEMBERS, - sMembers: SMEMBERS, - SMISMEMBER, - smIsMember: SMISMEMBER, - SMOVE, - sMove: SMOVE, - SORT_RO, - sortRo: SORT_RO, - SORT_STORE, - sortStore: SORT_STORE, - SORT, - sort: SORT, - SPOP, - sPop: SPOP, - SPUBLISH, - sPublish: SPUBLISH, - SRANDMEMBER_COUNT, - sRandMemberCount: SRANDMEMBER_COUNT, - SRANDMEMBER, - sRandMember: SRANDMEMBER, - SREM, - sRem: SREM, - SSCAN, - sScan: SSCAN, - STRLEN, - strLen: STRLEN, - SUNION, - sUnion: SUNION, - SUNIONSTORE, - sUnionStore: SUNIONSTORE, - TOUCH, - touch: TOUCH, - TTL, - ttl: TTL, - TYPE, - type: TYPE, - UNLINK, - unlink: UNLINK, - WATCH, - watch: WATCH, - XACK, - xAck: XACK, - XADD, - xAdd: XADD, - XAUTOCLAIM_JUSTID, - xAutoClaimJustId: XAUTOCLAIM_JUSTID, - XAUTOCLAIM, - xAutoClaim: XAUTOCLAIM, - XCLAIM, - xClaim: XCLAIM, - XCLAIM_JUSTID, - xClaimJustId: XCLAIM_JUSTID, - XDEL, - xDel: XDEL, - XGROUP_CREATE, - xGroupCreate: XGROUP_CREATE, - XGROUP_CREATECONSUMER, - xGroupCreateConsumer: XGROUP_CREATECONSUMER, - XGROUP_DELCONSUMER, - xGroupDelConsumer: XGROUP_DELCONSUMER, - XGROUP_DESTROY, - xGroupDestroy: XGROUP_DESTROY, - XGROUP_SETID, - xGroupSetId: XGROUP_SETID, - XINFO_CONSUMERS, - xInfoConsumers: XINFO_CONSUMERS, - XINFO_GROUPS, - xInfoGroups: XINFO_GROUPS, - XINFO_STREAM, - xInfoStream: XINFO_STREAM, - XLEN, - xLen: XLEN, - XPENDING_RANGE, - xPendingRange: XPENDING_RANGE, - XPENDING, - xPending: XPENDING, - XRANGE, - xRange: XRANGE, - XREAD, - xRead: XREAD, - XREADGROUP, - xReadGroup: XREADGROUP, - XREVRANGE, - xRevRange: XREVRANGE, - XSETID, - xSetId: XSETID, - XTRIM, - xTrim: XTRIM, - ZADD, - zAdd: ZADD, - ZCARD, - zCard: ZCARD, - ZCOUNT, - zCount: ZCOUNT, - ZDIFF_WITHSCORES, - zDiffWithScores: ZDIFF_WITHSCORES, - ZDIFF, - zDiff: ZDIFF, - ZDIFFSTORE, - zDiffStore: ZDIFFSTORE, - ZINCRBY, - zIncrBy: ZINCRBY, - ZINTER_WITHSCORES, - zInterWithScores: ZINTER_WITHSCORES, - ZINTER, - zInter: ZINTER, - ZINTERCARD, - zInterCard: ZINTERCARD, - ZINTERSTORE, - zInterStore: ZINTERSTORE, - ZLEXCOUNT, - zLexCount: ZLEXCOUNT, - ZMPOP, - zmPop: ZMPOP, - ZMSCORE, - zmScore: ZMSCORE, - ZPOPMAX_COUNT, - zPopMaxCount: ZPOPMAX_COUNT, - ZPOPMAX, - zPopMax: ZPOPMAX, - ZPOPMIN_COUNT, - zPopMinCount: ZPOPMIN_COUNT, - ZPOPMIN, - zPopMin: ZPOPMIN, - ZRANDMEMBER_COUNT_WITHSCORES, - zRandMemberCountWithScores: ZRANDMEMBER_COUNT_WITHSCORES, - ZRANDMEMBER_COUNT, - zRandMemberCount: ZRANDMEMBER_COUNT, - ZRANDMEMBER, - zRandMember: ZRANDMEMBER, - ZRANGE_WITHSCORES, - zRangeWithScores: ZRANGE_WITHSCORES, - ZRANGE, - zRange: ZRANGE, - ZRANGEBYLEX, - zRangeByLex: ZRANGEBYLEX, - ZRANGEBYSCORE_WITHSCORES, - zRangeByScoreWithScores: ZRANGEBYSCORE_WITHSCORES, - ZRANGEBYSCORE, - zRangeByScore: ZRANGEBYSCORE, - ZRANGESTORE, - zRangeStore: ZRANGESTORE, - ZRANK, - zRank: ZRANK, - ZREM, - zRem: ZREM, - ZREMRANGEBYLEX, - zRemRangeByLex: ZREMRANGEBYLEX, - ZREMRANGEBYRANK, - zRemRangeByRank: ZREMRANGEBYRANK, - ZREMRANGEBYSCORE, - zRemRangeByScore: ZREMRANGEBYSCORE, - ZREVRANK, - zRevRank: ZREVRANK, - ZSCAN, - zScan: ZSCAN, - ZSCORE, - zScore: ZSCORE, - ZUNION_WITHSCORES, - zUnionWithScores: ZUNION_WITHSCORES, - ZUNION, - zUnion: ZUNION, - ZUNIONSTORE, - zUnionStore: ZUNIONSTORE -}; diff --git a/packages/client/lib/cluster/index.spec.ts b/packages/client/lib/cluster/index.spec.ts index 8200375056a..4c06dc8c917 100644 --- a/packages/client/lib/cluster/index.spec.ts +++ b/packages/client/lib/cluster/index.spec.ts @@ -1,362 +1,362 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; -import RedisCluster from '.'; -import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; -import { commandOptions } from '../command-options'; -import { SQUARE_SCRIPT } from '../client/index.spec'; -import { RootNodesUnavailableError } from '../errors'; -import { spy } from 'sinon'; -import { promiseTimeout } from '../utils'; -import RedisClient from '../client'; - -describe('Cluster', () => { - testUtils.testWithCluster('sendCommand', async cluster => { - assert.equal( - await cluster.sendCommand(undefined, true, ['PING']), - 'PONG' - ); - }, GLOBAL.CLUSTERS.OPEN); - - testUtils.testWithCluster('isOpen', async cluster => { - assert.equal(cluster.isOpen, true); - await cluster.disconnect(); - assert.equal(cluster.isOpen, false); - }, GLOBAL.CLUSTERS.OPEN); - - testUtils.testWithCluster('connect should throw if already connected', async cluster => { - await assert.rejects(cluster.connect()); - }, GLOBAL.CLUSTERS.OPEN); - - testUtils.testWithCluster('multi', async cluster => { - const key = 'key'; - assert.deepEqual( - await cluster.multi() - .set(key, 'value') - .get(key) - .exec(), - ['OK', 'value'] - ); - }, GLOBAL.CLUSTERS.OPEN); - - testUtils.testWithCluster('scripts', async cluster => { - assert.equal( - await cluster.square(2), - 4 - ); - }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - scripts: { - square: SQUARE_SCRIPT - } - } - }); - - it('should throw RootNodesUnavailableError', async () => { - const cluster = RedisCluster.create({ - rootNodes: [] - }); - - try { - await assert.rejects( - cluster.connect(), - RootNodesUnavailableError - ); - } catch (err) { - await cluster.disconnect(); - throw err; - } - }); - - testUtils.testWithCluster('should handle live resharding', async cluster => { - const slot = 12539, - key = 'key', - value = 'value'; - await cluster.set(key, value); - - const importing = cluster.slots[0].master, - migrating = cluster.slots[slot].master, - [ importingClient, migratingClient ] = await Promise.all([ - cluster.nodeClient(importing), - cluster.nodeClient(migrating) - ]); - - await Promise.all([ - importingClient.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, migrating.id), - migratingClient.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, importing.id) - ]); - - // should be able to get the key from the migrating node - assert.equal( - await cluster.get(key), - value - ); - - await migratingClient.migrate( - importing.host, - importing.port, - key, - 0, - 10 - ); - - // should be able to get the key from the importing node using `ASKING` - assert.equal( - await cluster.get(key), - value - ); - - await Promise.all([ - importingClient.clusterSetSlot(slot, ClusterSlotStates.NODE, importing.id), - migratingClient.clusterSetSlot(slot, ClusterSlotStates.NODE, importing.id), - ]); - - // should handle `MOVED` errors - assert.equal( - await cluster.get(key), - value - ); - }, { - serverArguments: [], - numberOfMasters: 2 - }); - - testUtils.testWithCluster('getRandomNode should spread the the load evenly', async cluster => { - const totalNodes = cluster.masters.length + cluster.replicas.length, - ids = new Set(); - for (let i = 0; i < totalNodes; i++) { - ids.add(cluster.getRandomNode().id); - } +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; +// import RedisCluster from '.'; +// import { ClusterSlotStates } from '../commands/CLUSTER_SETSLOT'; +// import { commandOptions } from '../command-options'; +// import { SQUARE_SCRIPT } from '../client/index.spec'; +// import { RootNodesUnavailableError } from '../errors'; +// import { spy } from 'sinon'; +// import { promiseTimeout } from '../utils'; +// import RedisClient from '../client'; + +// describe('Cluster', () => { +// testUtils.testWithCluster('sendCommand', async cluster => { +// assert.equal( +// await cluster.sendCommand(undefined, true, ['PING']), +// 'PONG' +// ); +// }, GLOBAL.CLUSTERS.OPEN); + +// testUtils.testWithCluster('isOpen', async cluster => { +// assert.equal(cluster.isOpen, true); +// await cluster.disconnect(); +// assert.equal(cluster.isOpen, false); +// }, GLOBAL.CLUSTERS.OPEN); + +// testUtils.testWithCluster('connect should throw if already connected', async cluster => { +// await assert.rejects(cluster.connect()); +// }, GLOBAL.CLUSTERS.OPEN); + +// testUtils.testWithCluster('multi', async cluster => { +// const key = 'key'; +// assert.deepEqual( +// await cluster.multi() +// .set(key, 'value') +// .get(key) +// .exec(), +// ['OK', 'value'] +// ); +// }, GLOBAL.CLUSTERS.OPEN); + +// testUtils.testWithCluster('scripts', async cluster => { +// assert.equal( +// await cluster.square(2), +// 4 +// ); +// }, { +// ...GLOBAL.CLUSTERS.OPEN, +// clusterConfiguration: { +// scripts: { +// square: SQUARE_SCRIPT +// } +// } +// }); + +// it('should throw RootNodesUnavailableError', async () => { +// const cluster = RedisCluster.create({ +// rootNodes: [] +// }); + +// try { +// await assert.rejects( +// cluster.connect(), +// RootNodesUnavailableError +// ); +// } catch (err) { +// await cluster.disconnect(); +// throw err; +// } +// }); + +// testUtils.testWithCluster('should handle live resharding', async cluster => { +// const slot = 12539, +// key = 'key', +// value = 'value'; +// await cluster.set(key, value); + +// const importing = cluster.slots[0].master, +// migrating = cluster.slots[slot].master, +// [ importingClient, migratingClient ] = await Promise.all([ +// cluster.nodeClient(importing), +// cluster.nodeClient(migrating) +// ]); + +// await Promise.all([ +// importingClient.clusterSetSlot(slot, ClusterSlotStates.IMPORTING, migrating.id), +// migratingClient.clusterSetSlot(slot, ClusterSlotStates.MIGRATING, importing.id) +// ]); + +// // should be able to get the key from the migrating node +// assert.equal( +// await cluster.get(key), +// value +// ); + +// await migratingClient.migrate( +// importing.host, +// importing.port, +// key, +// 0, +// 10 +// ); + +// // should be able to get the key from the importing node using `ASKING` +// assert.equal( +// await cluster.get(key), +// value +// ); + +// await Promise.all([ +// importingClient.clusterSetSlot(slot, ClusterSlotStates.NODE, importing.id), +// migratingClient.clusterSetSlot(slot, ClusterSlotStates.NODE, importing.id), +// ]); + +// // should handle `MOVED` errors +// assert.equal( +// await cluster.get(key), +// value +// ); +// }, { +// serverArguments: [], +// numberOfMasters: 2 +// }); + +// testUtils.testWithCluster('getRandomNode should spread the the load evenly', async cluster => { +// const totalNodes = cluster.masters.length + cluster.replicas.length, +// ids = new Set(); +// for (let i = 0; i < totalNodes; i++) { +// ids.add(cluster.getRandomNode().id); +// } - assert.equal(ids.size, totalNodes); - }, GLOBAL.CLUSTERS.WITH_REPLICAS); - - testUtils.testWithCluster('getSlotRandomNode should spread the the load evenly', async cluster => { - const totalNodes = 1 + cluster.slots[0].replicas!.length, - ids = new Set(); - for (let i = 0; i < totalNodes; i++) { - ids.add(cluster.getSlotRandomNode(0).id); - } +// assert.equal(ids.size, totalNodes); +// }, GLOBAL.CLUSTERS.WITH_REPLICAS); + +// testUtils.testWithCluster('getSlotRandomNode should spread the the load evenly', async cluster => { +// const totalNodes = 1 + cluster.slots[0].replicas!.length, +// ids = new Set(); +// for (let i = 0; i < totalNodes; i++) { +// ids.add(cluster.getSlotRandomNode(0).id); +// } - assert.equal(ids.size, totalNodes); - }, GLOBAL.CLUSTERS.WITH_REPLICAS); - - testUtils.testWithCluster('cluster topology', async cluster => { - assert.equal(cluster.slots.length, 16384); - const { numberOfMasters, numberOfReplicas } = GLOBAL.CLUSTERS.WITH_REPLICAS; - assert.equal(cluster.shards.length, numberOfMasters); - assert.equal(cluster.masters.length, numberOfMasters); - assert.equal(cluster.replicas.length, numberOfReplicas * numberOfMasters); - assert.equal(cluster.nodeByAddress.size, numberOfMasters + numberOfMasters * numberOfReplicas); - }, GLOBAL.CLUSTERS.WITH_REPLICAS); - - testUtils.testWithCluster('getMasters should be backwards competiable (without `minimizeConnections`)', async cluster => { - const masters = cluster.getMasters(); - assert.ok(Array.isArray(masters)); - for (const master of masters) { - assert.equal(typeof master.id, 'string'); - assert.ok(master.client instanceof RedisClient); - } - }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - minimizeConnections: undefined // reset to default - } - }); - - testUtils.testWithCluster('getSlotMaster should be backwards competiable (without `minimizeConnections`)', async cluster => { - const master = cluster.getSlotMaster(0); - assert.equal(typeof master.id, 'string'); - assert.ok(master.client instanceof RedisClient); - }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - minimizeConnections: undefined // reset to default - } - }); - - testUtils.testWithCluster('should throw CROSSSLOT error', async cluster => { - await assert.rejects(cluster.mGet(['a', 'b'])); - }, GLOBAL.CLUSTERS.OPEN); - - testUtils.testWithCluster('should send commands with commandOptions to correct cluster slot (without redirections)', async cluster => { - // 'a' and 'b' hash to different cluster slots (see previous unit test) - // -> maxCommandRedirections 0: rejects on MOVED/ASK reply - await cluster.set(commandOptions({ isolated: true }), 'a', '1'), - await cluster.set(commandOptions({ isolated: true }), 'b', '2'), - - assert.equal(await cluster.get('a'), '1'); - assert.equal(await cluster.get('b'), '2'); - }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - maxCommandRedirections: 0 - } - }); - - describe('minimizeConnections', () => { - testUtils.testWithCluster('false', async cluster => { - for (const master of cluster.masters) { - assert.ok(master.client instanceof RedisClient); - } - }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - minimizeConnections: false - } - }); - - testUtils.testWithCluster('true', async cluster => { - for (const master of cluster.masters) { - assert.equal(master.client, undefined); - } - }, { - ...GLOBAL.CLUSTERS.OPEN, - clusterConfiguration: { - minimizeConnections: true - } - }); - }); - - describe('PubSub', () => { - testUtils.testWithCluster('subscribe & unsubscribe', async cluster => { - const listener = spy(); - - await cluster.subscribe('channel', listener); - - await Promise.all([ - waitTillBeenCalled(listener), - cluster.publish('channel', 'message') - ]); +// assert.equal(ids.size, totalNodes); +// }, GLOBAL.CLUSTERS.WITH_REPLICAS); + +// testUtils.testWithCluster('cluster topology', async cluster => { +// assert.equal(cluster.slots.length, 16384); +// const { numberOfMasters, numberOfReplicas } = GLOBAL.CLUSTERS.WITH_REPLICAS; +// assert.equal(cluster.shards.length, numberOfMasters); +// assert.equal(cluster.masters.length, numberOfMasters); +// assert.equal(cluster.replicas.length, numberOfReplicas * numberOfMasters); +// assert.equal(cluster.nodeByAddress.size, numberOfMasters + numberOfMasters * numberOfReplicas); +// }, GLOBAL.CLUSTERS.WITH_REPLICAS); + +// testUtils.testWithCluster('getMasters should be backwards competiable (without `minimizeConnections`)', async cluster => { +// const masters = cluster.getMasters(); +// assert.ok(Array.isArray(masters)); +// for (const master of masters) { +// assert.equal(typeof master.id, 'string'); +// assert.ok(master.client instanceof RedisClient); +// } +// }, { +// ...GLOBAL.CLUSTERS.OPEN, +// clusterConfiguration: { +// minimizeConnections: undefined // reset to default +// } +// }); + +// testUtils.testWithCluster('getSlotMaster should be backwards competiable (without `minimizeConnections`)', async cluster => { +// const master = cluster.getSlotMaster(0); +// assert.equal(typeof master.id, 'string'); +// assert.ok(master.client instanceof RedisClient); +// }, { +// ...GLOBAL.CLUSTERS.OPEN, +// clusterConfiguration: { +// minimizeConnections: undefined // reset to default +// } +// }); + +// testUtils.testWithCluster('should throw CROSSSLOT error', async cluster => { +// await assert.rejects(cluster.mGet(['a', 'b'])); +// }, GLOBAL.CLUSTERS.OPEN); + +// testUtils.testWithCluster('should send commands with commandOptions to correct cluster slot (without redirections)', async cluster => { +// // 'a' and 'b' hash to different cluster slots (see previous unit test) +// // -> maxCommandRedirections 0: rejects on MOVED/ASK reply +// await cluster.set(commandOptions({ isolated: true }), 'a', '1'), +// await cluster.set(commandOptions({ isolated: true }), 'b', '2'), + +// assert.equal(await cluster.get('a'), '1'); +// assert.equal(await cluster.get('b'), '2'); +// }, { +// ...GLOBAL.CLUSTERS.OPEN, +// clusterConfiguration: { +// maxCommandRedirections: 0 +// } +// }); + +// describe('minimizeConnections', () => { +// testUtils.testWithCluster('false', async cluster => { +// for (const master of cluster.masters) { +// assert.ok(master.client instanceof RedisClient); +// } +// }, { +// ...GLOBAL.CLUSTERS.OPEN, +// clusterConfiguration: { +// minimizeConnections: false +// } +// }); + +// testUtils.testWithCluster('true', async cluster => { +// for (const master of cluster.masters) { +// assert.equal(master.client, undefined); +// } +// }, { +// ...GLOBAL.CLUSTERS.OPEN, +// clusterConfiguration: { +// minimizeConnections: true +// } +// }); +// }); + +// describe('PubSub', () => { +// testUtils.testWithCluster('subscribe & unsubscribe', async cluster => { +// const listener = spy(); + +// await cluster.subscribe('channel', listener); + +// await Promise.all([ +// waitTillBeenCalled(listener), +// cluster.publish('channel', 'message') +// ]); - assert.ok(listener.calledOnceWithExactly('message', 'channel')); +// assert.ok(listener.calledOnceWithExactly('message', 'channel')); - await cluster.unsubscribe('channel', listener); +// await cluster.unsubscribe('channel', listener); - assert.equal(cluster.pubSubNode, undefined); - }, GLOBAL.CLUSTERS.OPEN); +// assert.equal(cluster.pubSubNode, undefined); +// }, GLOBAL.CLUSTERS.OPEN); - testUtils.testWithCluster('psubscribe & punsubscribe', async cluster => { - const listener = spy(); +// testUtils.testWithCluster('psubscribe & punsubscribe', async cluster => { +// const listener = spy(); - await cluster.pSubscribe('channe*', listener); +// await cluster.pSubscribe('channe*', listener); - await Promise.all([ - waitTillBeenCalled(listener), - cluster.publish('channel', 'message') - ]); +// await Promise.all([ +// waitTillBeenCalled(listener), +// cluster.publish('channel', 'message') +// ]); - assert.ok(listener.calledOnceWithExactly('message', 'channel')); - - await cluster.pUnsubscribe('channe*', listener); - - assert.equal(cluster.pubSubNode, undefined); - }, GLOBAL.CLUSTERS.OPEN); - - testUtils.testWithCluster('should move listeners when PubSub node disconnects from the cluster', async cluster => { - const listener = spy(); - await cluster.subscribe('channel', listener); - - assert.ok(cluster.pubSubNode); - const [ migrating, importing ] = cluster.masters[0].address === cluster.pubSubNode.address ? - cluster.masters : - [cluster.masters[1], cluster.masters[0]], - [ migratingClient, importingClient ] = await Promise.all([ - cluster.nodeClient(migrating), - cluster.nodeClient(importing) - ]); - - const range = cluster.slots[0].master === migrating ? { - key: 'bar', // 5061 - start: 0, - end: 8191 - } : { - key: 'foo', // 12182 - start: 8192, - end: 16383 - }; - - await Promise.all([ - migratingClient.clusterDelSlotsRange(range), - importingClient.clusterDelSlotsRange(range), - importingClient.clusterAddSlotsRange(range) - ]); - - // wait for migrating node to be notified about the new topology - while ((await migratingClient.clusterInfo()).state !== 'ok') { - await promiseTimeout(50); - } - - // make sure to cause `MOVED` error - await cluster.get(range.key); - - await Promise.all([ - cluster.publish('channel', 'message'), - waitTillBeenCalled(listener) - ]); +// assert.ok(listener.calledOnceWithExactly('message', 'channel')); + +// await cluster.pUnsubscribe('channe*', listener); + +// assert.equal(cluster.pubSubNode, undefined); +// }, GLOBAL.CLUSTERS.OPEN); + +// testUtils.testWithCluster('should move listeners when PubSub node disconnects from the cluster', async cluster => { +// const listener = spy(); +// await cluster.subscribe('channel', listener); + +// assert.ok(cluster.pubSubNode); +// const [ migrating, importing ] = cluster.masters[0].address === cluster.pubSubNode.address ? +// cluster.masters : +// [cluster.masters[1], cluster.masters[0]], +// [ migratingClient, importingClient ] = await Promise.all([ +// cluster.nodeClient(migrating), +// cluster.nodeClient(importing) +// ]); + +// const range = cluster.slots[0].master === migrating ? { +// key: 'bar', // 5061 +// start: 0, +// end: 8191 +// } : { +// key: 'foo', // 12182 +// start: 8192, +// end: 16383 +// }; + +// await Promise.all([ +// migratingClient.clusterDelSlotsRange(range), +// importingClient.clusterDelSlotsRange(range), +// importingClient.clusterAddSlotsRange(range) +// ]); + +// // wait for migrating node to be notified about the new topology +// while ((await migratingClient.clusterInfo()).state !== 'ok') { +// await promiseTimeout(50); +// } + +// // make sure to cause `MOVED` error +// await cluster.get(range.key); + +// await Promise.all([ +// cluster.publish('channel', 'message'), +// waitTillBeenCalled(listener) +// ]); - assert.ok(listener.calledOnceWithExactly('message', 'channel')); - }, { - serverArguments: [], - numberOfMasters: 2, - minimumDockerVersion: [7] - }); - - testUtils.testWithCluster('ssubscribe & sunsubscribe', async cluster => { - const listener = spy(); - - await cluster.sSubscribe('channel', listener); - - await Promise.all([ - waitTillBeenCalled(listener), - cluster.sPublish('channel', 'message') - ]); +// assert.ok(listener.calledOnceWithExactly('message', 'channel')); +// }, { +// serverArguments: [], +// numberOfMasters: 2, +// minimumDockerVersion: [7] +// }); + +// testUtils.testWithCluster('ssubscribe & sunsubscribe', async cluster => { +// const listener = spy(); + +// await cluster.sSubscribe('channel', listener); + +// await Promise.all([ +// waitTillBeenCalled(listener), +// cluster.sPublish('channel', 'message') +// ]); - assert.ok(listener.calledOnceWithExactly('message', 'channel')); - - await cluster.sUnsubscribe('channel', listener); - - // 10328 is the slot of `channel` - assert.equal(cluster.slots[10328].master.pubSubClient, undefined); - }, { - ...GLOBAL.CLUSTERS.OPEN, - minimumDockerVersion: [7] - }); - - testUtils.testWithCluster('should handle sharded-channel-moved events', async cluster => { - const SLOT = 10328, - migrating = cluster.slots[SLOT].master, - importing = cluster.masters.find(master => master !== migrating)!, - [ migratingClient, importingClient ] = await Promise.all([ - cluster.nodeClient(migrating), - cluster.nodeClient(importing) - ]); - - await Promise.all([ - migratingClient.clusterDelSlots(SLOT), - importingClient.clusterDelSlots(SLOT), - importingClient.clusterAddSlots(SLOT) - ]); - - // wait for migrating node to be notified about the new topology - while ((await migratingClient.clusterInfo()).state !== 'ok') { - await promiseTimeout(50); - } - - const listener = spy(); - - // will trigger `MOVED` error - await cluster.sSubscribe('channel', listener); - - await Promise.all([ - waitTillBeenCalled(listener), - cluster.sPublish('channel', 'message') - ]); +// assert.ok(listener.calledOnceWithExactly('message', 'channel')); + +// await cluster.sUnsubscribe('channel', listener); + +// // 10328 is the slot of `channel` +// assert.equal(cluster.slots[10328].master.pubSubClient, undefined); +// }, { +// ...GLOBAL.CLUSTERS.OPEN, +// minimumDockerVersion: [7] +// }); + +// testUtils.testWithCluster('should handle sharded-channel-moved events', async cluster => { +// const SLOT = 10328, +// migrating = cluster.slots[SLOT].master, +// importing = cluster.masters.find(master => master !== migrating)!, +// [ migratingClient, importingClient ] = await Promise.all([ +// cluster.nodeClient(migrating), +// cluster.nodeClient(importing) +// ]); + +// await Promise.all([ +// migratingClient.clusterDelSlots(SLOT), +// importingClient.clusterDelSlots(SLOT), +// importingClient.clusterAddSlots(SLOT) +// ]); + +// // wait for migrating node to be notified about the new topology +// while ((await migratingClient.clusterInfo()).state !== 'ok') { +// await promiseTimeout(50); +// } + +// const listener = spy(); + +// // will trigger `MOVED` error +// await cluster.sSubscribe('channel', listener); + +// await Promise.all([ +// waitTillBeenCalled(listener), +// cluster.sPublish('channel', 'message') +// ]); - assert.ok(listener.calledOnceWithExactly('message', 'channel')); - }, { - serverArguments: [], - minimumDockerVersion: [7] - }); - }); -}); +// assert.ok(listener.calledOnceWithExactly('message', 'channel')); +// }, { +// serverArguments: [], +// minimumDockerVersion: [7] +// }); +// }); +// }); diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 7e486a376b6..96560fdaf05 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -1,424 +1,549 @@ -import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, RedisFunction } from '../commands'; -import { ClientCommandOptions, RedisClientOptions, RedisClientType, WithFunctions, WithModules, WithScripts } from '../client'; -import RedisClusterSlots, { NodeAddressMap, ShardNode } from './cluster-slots'; -import { attachExtensions, transformCommandReply, attachCommands, transformCommandArguments } from '../commander'; +import { ClientCommandOptions, RedisClientOptions, RedisClientType } from '../client'; +import { Command, CommandArguments, CommanderConfig, CommandPolicies, CommandSignature, CommandWithPoliciesSignature, Flags, RedisArgument, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, TransformReply } from '../RESP/types'; +import COMMANDS from '../commands'; import { EventEmitter } from 'events'; -import RedisClusterMultiCommand, { InstantiableRedisClusterMultiCommandType, RedisClusterMultiCommandType } from './multi-command'; -import { RedisMultiQueuedCommand } from '../multi-command'; +import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; +import RedisClusterSlots, { NodeAddressMap, ShardNode } from './cluster-slots'; +// import RedisClusterMultiCommand, { InstantiableRedisClusterMultiCommandType, RedisClusterMultiCommandType } from './multi-command'; +// import { RedisMultiQueuedCommand } from '../multi-command'; import { PubSubListener } from '../client/pub-sub'; import { ErrorReply } from '../errors'; export type RedisClusterClientOptions = Omit< - RedisClientOptions, - 'modules' | 'functions' | 'scripts' | 'database' + RedisClientOptions, + 'modules' | 'functions' | 'scripts' | 'database' | 'RESP' >; export interface RedisClusterOptions< - M extends RedisModules = Record, - F extends RedisFunctions = Record, - S extends RedisScripts = Record -> extends RedisExtensions { - /** - * 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. - */ - rootNodes: Array; - /** - * Default values used for every client in the cluster. Use this to specify global values, - * for example: ACL credentials, timeouts, TLS configuration etc. - */ - defaults?: Partial; - /** - * 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; - /** - * 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; + M extends RedisModules = RedisModules, + F extends RedisFunctions = RedisFunctions, + S extends RedisScripts = RedisScripts, + RESP extends RespVersions = RespVersions +> extends CommanderConfig { + /** + * 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. + */ + rootNodes: Array; + /** + * Default values used for every client in the cluster. Use this to specify global values, + * for example: ACL credentials, timeouts, TLS configuration etc. + */ + defaults?: Partial; + /** + * 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. + */ + // TODO: replicas only mode? + useReplicas?: boolean; + /** + * The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors. + */ + maxCommandRedirections?: number; + /** + * 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; } -type WithCommands = { - [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>; +type WithCommands< + RESP extends RespVersions, + FLAGS extends Flags, + POLICIES extends CommandPolicies +> = { + [P in keyof typeof COMMANDS]: CommandWithPoliciesSignature<(typeof COMMANDS)[P], RESP, FLAGS, POLICIES>; }; export type RedisClusterType< - M extends RedisModules = Record, - F extends RedisFunctions = Record, - S extends RedisScripts = Record -> = RedisCluster & WithCommands & WithModules & WithFunctions & WithScripts; - -export default class RedisCluster< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> extends EventEmitter { - static extractFirstKey( - command: RedisCommand, - originalArgs: Array, - redisArgs: RedisCommandArguments - ): RedisCommandArgument | undefined { - if (command.FIRST_KEY_INDEX === undefined) { - return undefined; - } else if (typeof command.FIRST_KEY_INDEX === 'number') { - return redisArgs[command.FIRST_KEY_INDEX]; - } - - return command.FIRST_KEY_INDEX(...originalArgs); - } - - static create< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(options?: RedisClusterOptions): RedisClusterType { - return new (attachExtensions({ - BaseClass: RedisCluster, - modulesExecutor: RedisCluster.prototype.commandsExecutor, - modules: options?.modules, - functionsExecutor: RedisCluster.prototype.functionsExecutor, - functions: options?.functions, - scriptsExecutor: RedisCluster.prototype.scriptsExecutor, - scripts: options?.scripts - }))(options); - } - - readonly #options: RedisClusterOptions; - - readonly #slots: RedisClusterSlots; - - get slots() { - return this.#slots.slots; - } - - get shards() { - return this.#slots.shards; - } - - get masters() { - return this.#slots.masters; - } - - get replicas() { - return this.#slots.replicas; - } - - get nodeByAddress() { - return this.#slots.nodeByAddress; - } - - get pubSubNode() { - return this.#slots.pubSubNode; - } - - readonly #Multi: InstantiableRedisClusterMultiCommandType; - - get isOpen() { - return this.#slots.isOpen; - } - - constructor(options: RedisClusterOptions) { - super(); - - this.#options = options; - this.#slots = new RedisClusterSlots(options, this.emit.bind(this)); - this.#Multi = RedisClusterMultiCommand.extend(options); - } - - duplicate(overrides?: Partial>): RedisClusterType { - return new (Object.getPrototypeOf(this).constructor)({ - ...this.#options, - ...overrides - }); - } - - connect() { - return this.#slots.connect(); - } + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + FLAGS extends Flags = {}, + POLICIES extends CommandPolicies = {} +> = RedisCluster & WithCommands; +// & WithModules & WithFunctions & WithScripts + +export interface ClusterCommandOptions extends ClientCommandOptions { + policies?: CommandPolicies; +} - async commandsExecutor( - command: C, - args: Array - ): Promise> { - const { jsArgs, args: redisArgs, options } = transformCommandArguments(command, args); - return transformCommandReply( - command, - await this.sendCommand( - RedisCluster.extractFirstKey(command, jsArgs, redisArgs), - command.IS_READ_ONLY, - redisArgs, - options - ), - redisArgs.preserve - ); - } +type ProxyCluster = RedisCluster & { commandOptions?: ClusterCommandOptions }; - async sendCommand( - firstKey: RedisCommandArgument | undefined, - isReadonly: boolean | undefined, - args: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - return this.#execute( - firstKey, - isReadonly, - client => client.sendCommand(args, options) - ); - } +type NamespaceProxyCluster = { self: ProxyCluster }; - async functionsExecutor( - fn: F, - args: Array, - name: string, - ): Promise> { - const { args: redisArgs, options } = transformCommandArguments(fn, args); - return transformCommandReply( - fn, - await this.executeFunction( - name, - fn, - args, - redisArgs, - options - ), - redisArgs.preserve +export default class RedisCluster< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions, + FLAGS extends Flags, + POLICIES extends CommandPolicies +> extends EventEmitter { + private static _extractFirstKey( + command: C, + args: Parameters, + redisArgs: Array + ): RedisArgument | undefined { + if (command.FIRST_KEY_INDEX === undefined) { + return undefined; + } else if (typeof command.FIRST_KEY_INDEX === 'number') { + return redisArgs[command.FIRST_KEY_INDEX]; + } + + return command.FIRST_KEY_INDEX(...args); + } + + private static _createCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: ProxyCluster) { + const args = command.transformArguments.apply(undefined, arguments as any), + firstKey = RedisCluster._extractFirstKey( + command, + arguments as any, + args + ), + reply = await this.sendCommand( + firstKey, + command.IS_READ_ONLY, + args, + this.commandOptions, + command.POLICIES ); - } - async executeFunction( - name: string, - fn: RedisFunction, - originalArgs: Array, - redisArgs: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - return this.#execute( - RedisCluster.extractFirstKey(fn, originalArgs, redisArgs), - fn.IS_READ_ONLY, - client => client.executeFunction(name, fn, redisArgs, options) + return transformReply ? + transformReply(reply, args.preserve) : + reply; + }; + } + + private static _createModuleCommand(command: Command, resp: RespVersions) { + const transformReply = getTransformReply(command, resp); + return async function (this: NamespaceProxyCluster) { + const args = command.transformArguments.apply(undefined, arguments as any), + firstKey = RedisCluster._extractFirstKey( + command, + arguments as any, + args + ), + reply = await this.self.sendCommand( + firstKey, + command.IS_READ_ONLY, + args, + this.self.commandOptions, + command.POLICIES ); - } - async scriptsExecutor(script: S, args: Array): Promise> { - const { args: redisArgs, options } = transformCommandArguments(script, args); - return transformCommandReply( - script, - await this.executeScript( - script, - args, - redisArgs, - options - ), - redisArgs.preserve + return transformReply ? + transformReply(reply, args.preserve) : + reply; + }; + } + + private static _createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { + const prefix = functionArgumentsPrefix(name, fn), + transformReply = getTransformReply(fn, resp); + return async function (this: NamespaceProxyCluster) { + const fnArgs = fn.transformArguments.apply(undefined, arguments as any), + args = prefix.concat(fnArgs), + firstKey = RedisCluster._extractFirstKey( + fn, + arguments as any, + args + ), + reply = await this.self.sendCommand( + firstKey, + fn.IS_READ_ONLY, + args, + this.self.commandOptions, + fn.POLICIES ); - } - async executeScript( - script: RedisScript, - originalArgs: Array, - redisArgs: RedisCommandArguments, - options?: ClientCommandOptions - ): Promise { - return this.#execute( - RedisCluster.extractFirstKey(script, originalArgs, redisArgs), - script.IS_READ_ONLY, - client => client.executeScript(script, redisArgs, options) + return transformReply ? + transformReply(reply, fnArgs.preserve) : + reply; + }; + } + + private static _createScriptCommand(script: RedisScript, resp: RespVersions) { + const prefix = scriptArgumentsPrefix(script), + transformReply = getTransformReply(script, resp); + return async function (this: ProxyCluster) { + const scriptArgs = script.transformArguments.apply(undefined, arguments as any), + args = prefix.concat(scriptArgs), + firstKey = RedisCluster._extractFirstKey( + script, + arguments as any, + args + ), + reply = await this.sendCommand( + firstKey, + script.IS_READ_ONLY, + args, + this.commandOptions, + script.POLICIES ); - } - async #execute( - firstKey: RedisCommandArgument | undefined, - isReadonly: boolean | undefined, - executor: (client: RedisClientType) => Promise - ): Promise { - const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16; - let client = await this.#slots.getClient(firstKey, isReadonly); - for (let i = 0;; i++) { - try { - return await executor(client); - } catch (err) { - if (++i > maxCommandRedirections || !(err instanceof ErrorReply)) { - throw err; - } - - if (err.message.startsWith('ASK')) { - const address = err.message.substring(err.message.lastIndexOf(' ') + 1); - let redirectTo = await this.#slots.getMasterByAddress(address); - if (!redirectTo) { - await this.#slots.rediscover(client); - redirectTo = await this.#slots.getMasterByAddress(address); - } - - if (!redirectTo) { - throw new Error(`Cannot find node ${address}`); - } - - await redirectTo.asking(); - client = redirectTo; - continue; - } else if (err.message.startsWith('MOVED')) { - await this.#slots.rediscover(client); - client = await this.#slots.getClient(firstKey, isReadonly); - continue; - } - - throw err; - } + return transformReply ? + transformReply(reply, scriptArgs.preserve) : + reply; + }; + } + + static factory< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2 + >(config?: CommanderConfig) { + const Cluster = attachConfig({ + BaseClass: RedisCluster, + commands: COMMANDS, + createCommand: RedisCluster._createCommand, + createFunctionCommand: RedisCluster._createFunctionCommand, + createModuleCommand: RedisCluster._createModuleCommand, + createScriptCommand: RedisCluster._createScriptCommand, + config + }); + + // Client.prototype.Multi = RedisClientMultiCommand.extend(config); + + return (options?: Omit>) => { + // returning a proxy of the client to prevent the namespaces.self to leak between proxies + // namespaces will be bootstraped on first access per proxy + return Object.create(new Cluster(options)) as RedisClusterType; + }; + } + + static create< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2 + >(options?: RedisClusterOptions) { + return RedisCluster.factory(options)(options); + } + + private readonly _options: RedisClusterOptions; + + private readonly _slots: RedisClusterSlots; + + /** + * 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() { + return this._slots.slots; + } + + /** + * An array of cluster shards, 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 shards() { + return this._slots.shards; + } + + /** + * An array of the cluster masters. + * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific master node. + */ + get masters() { + return this._slots.masters; + } + + /** + * An array of the cluster replicas. + * Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific replica node. + */ + get replicas() { + return this._slots.replicas; + } + + /** + * A map form a node address (`:`) 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() { + return this._slots.nodeByAddress; + } + + /** + * The current pub/sub node. + */ + get pubSubNode() { + return this._slots.pubSubNode; + } + + // readonly #Multi: InstantiableRedisClusterMultiCommandType; + + get isOpen() { + return this._slots.isOpen; + } + + constructor(options: RedisClusterOptions) { + super(); + + this._options = options; + this._slots = new RedisClusterSlots(options, this.emit.bind(this)); + // this.#Multi = RedisClusterMultiCommand.extend(options); + } + + duplicate(overrides?: Partial>): RedisClusterType { + return new (Object.getPrototypeOf(this).constructor)({ + ...this._options, + ...overrides + }); + } + + connect() { + return this._slots.connect(); + } + + withCommandOptions(options: T) { + const proxy = Object.create(this); + proxy.commandOptions = options; + return proxy as RedisClusterType< + M, + F, + S, + RESP, + T['flags'] extends Flags ? T['flags'] : {}, + T['policies'] extends CommandPolicies ? T['policies'] : {} + >; + } + + private _commandOptionsProxy< + K extends keyof ClusterCommandOptions, + V extends ClusterCommandOptions[K] + >( + key: K, + value: V + ) { + const proxy = Object.create(this); + proxy.commandOptions = Object.create((this as unknown as ProxyCluster).commandOptions ?? null); + proxy.commandOptions[key] = value; + return proxy as RedisClusterType< + M, + F, + S, + RESP, + K extends 'flags' ? V extends Flags ? V : {} : FLAGS, + K extends 'policies' ? V extends CommandPolicies ? V : {} : POLICIES + >; + } + + /** + * Override the `flags` command option + */ + withFlags(flags: FLAGS) { + return this._commandOptionsProxy('flags', flags); + } + + /** + * Override the `policies` command option + * TODO + */ + withPolicies (policies: POLICIES) { + return this._commandOptionsProxy('policies', policies); + } + + async sendCommand( + firstKey: RedisArgument | undefined, + isReadonly: boolean | undefined, + args: CommandArguments, + options?: ClusterCommandOptions, + deafultPolicies?: CommandPolicies + ): Promise { + // const requestPolicy = options?.policies?.request ?? deafultPolicies?.request, + // responsePolicy = options?.policies?.response ?? deafultPolicies?.response; + + const maxCommandRedirections = this._options.maxCommandRedirections ?? 16; + let client = await this._slots.getClient(firstKey, isReadonly); + for (let i = 0; ; i++) { + try { + return await client.sendCommand(args, options); + } catch (err) { + // TODO: error class + if (++i > maxCommandRedirections || !(err instanceof Error)) { + throw err; } - } - - MULTI(routing?: RedisCommandArgument): RedisClusterMultiCommandType { - return new this.#Multi( - (commands: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => { - return this.#execute( - firstKey, - false, - client => client.multiExecutor(commands, undefined, chainId) - ); - }, - routing - ); - } - - multi = this.MULTI; - - async SUBSCRIBE( - channels: string | Array, - listener: PubSubListener, - bufferMode?: T - ) { - return (await this.#slots.getPubSubClient()) - .SUBSCRIBE(channels, listener, bufferMode); - } - - subscribe = this.SUBSCRIBE; - - async UNSUBSCRIBE( - channels?: string | Array, - listener?: PubSubListener, - bufferMode?: T - ) { - return this.#slots.executeUnsubscribeCommand(client => - client.UNSUBSCRIBE(channels, listener, bufferMode) - ); - } - - unsubscribe = this.UNSUBSCRIBE; - - async PSUBSCRIBE( - patterns: string | Array, - listener: PubSubListener, - bufferMode?: T - ) { - return (await this.#slots.getPubSubClient()) - .PSUBSCRIBE(patterns, listener, bufferMode); - } - - pSubscribe = this.PSUBSCRIBE; - - async PUNSUBSCRIBE( - patterns?: string | Array, - listener?: PubSubListener, - bufferMode?: T - ) { - return this.#slots.executeUnsubscribeCommand(client => - client.PUNSUBSCRIBE(patterns, listener, bufferMode) - ); - } - pUnsubscribe = this.PUNSUBSCRIBE; - - async SSUBSCRIBE( - channels: string | Array, - listener: PubSubListener, - bufferMode?: T - ) { - const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16, - firstChannel = Array.isArray(channels) ? channels[0] : channels; - let client = await this.#slots.getShardedPubSubClient(firstChannel); - for (let i = 0;; i++) { - try { - return await client.SSUBSCRIBE(channels, listener, bufferMode); - } catch (err) { - if (++i > maxCommandRedirections || !(err instanceof ErrorReply)) { - throw err; - } - - if (err.message.startsWith('MOVED')) { - await this.#slots.rediscover(client); - client = await this.#slots.getShardedPubSubClient(firstChannel); - continue; - } - - throw err; - } + if (err.message.startsWith('ASK')) { + const address = err.message.substring(err.message.lastIndexOf(' ') + 1); + let redirectTo = await this._slots.getMasterByAddress(address); + if (!redirectTo) { + await this._slots.rediscover(client); + redirectTo = await this._slots.getMasterByAddress(address); + } + + if (!redirectTo) { + throw new Error(`Cannot find node ${address}`); + } + + await redirectTo.asking(); + client = redirectTo; + continue; + } else if (err.message.startsWith('MOVED')) { + await this._slots.rediscover(client); + client = await this._slots.getClient(firstKey, isReadonly); + continue; } - } - - sSubscribe = this.SSUBSCRIBE; - - SUNSUBSCRIBE( - channels: string | Array, - listener: PubSubListener, - bufferMode?: T - ) { - return this.#slots.executeShardedUnsubscribeCommand( - Array.isArray(channels) ? channels[0] : channels, - client => client.SUNSUBSCRIBE(channels, listener, bufferMode) - ); - } - - sUnsubscribe = this.SUNSUBSCRIBE; - - quit(): Promise { - return this.#slots.quit(); - } - - disconnect(): Promise { - return this.#slots.disconnect(); - } - nodeClient(node: ShardNode) { - return this.#slots.nodeClient(node); - } - - getRandomNode() { - return this.#slots.getRandomNode(); - } - - getSlotRandomNode(slot: number) { - return this.#slots.getSlotRandomNode(slot); - } + throw err; + } + } + } + + // MULTI(routing?: RedisCommandArgument): RedisClusterMultiCommandType { + // return new this.#Multi( + // (commands: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => { + // return this.#execute( + // firstKey, + // false, + // client => client.multiExecutor(commands, undefined, chainId) + // ); + // }, + // routing + // ); + // } + + // multi = this.MULTI; + + async SUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return (await this._slots.getPubSubClient()) + .SUBSCRIBE(channels, listener, bufferMode); + } + + subscribe = this.SUBSCRIBE; + + async UNSUBSCRIBE( + channels?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this._slots.executeUnsubscribeCommand(client => + client.UNSUBSCRIBE(channels, listener, bufferMode) + ); + } + + unsubscribe = this.UNSUBSCRIBE; + + async PSUBSCRIBE( + patterns: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return (await this._slots.getPubSubClient()) + .PSUBSCRIBE(patterns, listener, bufferMode); + } + + pSubscribe = this.PSUBSCRIBE; + + async PUNSUBSCRIBE( + patterns?: string | Array, + listener?: PubSubListener, + bufferMode?: T + ) { + return this._slots.executeUnsubscribeCommand(client => + client.PUNSUBSCRIBE(patterns, listener, bufferMode) + ); + } + + pUnsubscribe = this.PUNSUBSCRIBE; + + async SSUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + const maxCommandRedirections = this._options.maxCommandRedirections ?? 16, + firstChannel = Array.isArray(channels) ? channels[0] : channels; + let client = await this._slots.getShardedPubSubClient(firstChannel); + for (let i = 0; ; i++) { + try { + return await client.SSUBSCRIBE(channels, listener, bufferMode); + } catch (err) { + if (++i > maxCommandRedirections || !(err instanceof ErrorReply)) { + throw err; + } - /** - * @deprecated use `.masters` instead - */ - getMasters() { - return this.masters; - } + if (err.message.startsWith('MOVED')) { + await this._slots.rediscover(client); + client = await this._slots.getShardedPubSubClient(firstChannel); + continue; + } - /** - * @deprecated use `.slots[]` instead - */ - getSlotMaster(slot: number) { - return this.slots[slot].master; - } + throw err; + } + } + } + + sSubscribe = this.SSUBSCRIBE; + + SUNSUBSCRIBE( + channels: string | Array, + listener: PubSubListener, + bufferMode?: T + ) { + return this._slots.executeShardedUnsubscribeCommand( + Array.isArray(channels) ? channels[0] : channels, + client => client.SUNSUBSCRIBE(channels, listener, bufferMode) + ); + } + + sUnsubscribe = this.SUNSUBSCRIBE; + + // quit(): Promise { + // return this.#slots.quit(); + // } + + disconnect(): Promise { + return this._slots.disconnect(); + } + + nodeClient(node: ShardNode) { + return this._slots.nodeClient(node); + } + + /** + * Returns a random node from the cluster. + * Userful for running "forward" commands (like PUBLISH) on a random node. + */ + getRandomNode() { + return this._slots.getRandomNode(); + } + + /** + * Get a random node from a slot. + * Useful for running readonly commands on a slot. + */ + getSlotRandomNode(slot: number) { + return this._slots.getSlotRandomNode(slot); + } + + /** + * @deprecated use `.masters` instead + * TODO + */ + getMasters() { + return this.masters; + } + + /** + * @deprecated use `.slots[]` instead + * TODO + */ + getSlotMaster(slot: number) { + return this.slots[slot].master; + } } - -attachCommands({ - BaseClass: RedisCluster, - commands: COMMANDS, - executor: RedisCluster.prototype.commandsExecutor -}); diff --git a/packages/client/lib/cluster/multi-command.ts b/packages/client/lib/cluster/multi-command.ts index ef3c7590ec7..379af544af1 100644 --- a/packages/client/lib/cluster/multi-command.ts +++ b/packages/client/lib/cluster/multi-command.ts @@ -1,141 +1,141 @@ -import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, ExcludeMappedString, RedisFunction } from '../commands'; -import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; -import { attachCommands, attachExtensions } from '../commander'; -import RedisCluster from '.'; - -type RedisClusterMultiCommandSignature< - C extends RedisCommand, - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = (...args: Parameters) => RedisClusterMultiCommandType; - -type WithCommands< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = { - [P in keyof typeof COMMANDS]: RedisClusterMultiCommandSignature<(typeof COMMANDS)[P], M, F, S>; -}; - -type WithModules< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = { - [P in keyof M as ExcludeMappedString

]: { - [C in keyof M[P] as ExcludeMappedString]: RedisClusterMultiCommandSignature; - }; -}; - -type WithFunctions< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = { - [P in keyof F as ExcludeMappedString

]: { - [FF in keyof F[P] as ExcludeMappedString]: RedisClusterMultiCommandSignature; - }; -}; - -type WithScripts< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = { - [P in keyof S as ExcludeMappedString

]: RedisClusterMultiCommandSignature; -}; - -export type RedisClusterMultiCommandType< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = RedisClusterMultiCommand & WithCommands & WithModules & WithFunctions & WithScripts; - -export type InstantiableRedisClusterMultiCommandType< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts -> = new (...args: ConstructorParameters) => RedisClusterMultiCommandType; - -export type RedisClusterMultiExecutor = (queue: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => Promise>; - -export default class RedisClusterMultiCommand { - readonly #multi = new RedisMultiCommand(); - readonly #executor: RedisClusterMultiExecutor; - #firstKey: RedisCommandArgument | undefined; - - static extend< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(extensions?: RedisExtensions): InstantiableRedisClusterMultiCommandType { - return attachExtensions({ - BaseClass: RedisClusterMultiCommand, - modulesExecutor: RedisClusterMultiCommand.prototype.commandsExecutor, - modules: extensions?.modules, - functionsExecutor: RedisClusterMultiCommand.prototype.functionsExecutor, - functions: extensions?.functions, - scriptsExecutor: RedisClusterMultiCommand.prototype.scriptsExecutor, - scripts: extensions?.scripts - }); - } - - constructor(executor: RedisClusterMultiExecutor, firstKey?: RedisCommandArgument) { - this.#executor = executor; - this.#firstKey = firstKey; - } - - commandsExecutor(command: RedisCommand, args: Array): this { - const transformedArguments = command.transformArguments(...args); - this.#firstKey ??= RedisCluster.extractFirstKey(command, args, transformedArguments); - return this.addCommand(undefined, transformedArguments, command.transformReply); - } - - addCommand( - firstKey: RedisCommandArgument | undefined, - args: RedisCommandArguments, - transformReply?: RedisCommand['transformReply'] - ): this { - this.#firstKey ??= firstKey; - this.#multi.addCommand(args, transformReply); - return this; - } - - functionsExecutor(fn: RedisFunction, args: Array, name: string): this { - const transformedArguments = this.#multi.addFunction(name, fn, args); - this.#firstKey ??= RedisCluster.extractFirstKey(fn, args, transformedArguments); - return this; - } - - scriptsExecutor(script: RedisScript, args: Array): this { - const transformedArguments = this.#multi.addScript(script, args); - this.#firstKey ??= RedisCluster.extractFirstKey(script, args, transformedArguments); - return this; - } - - async exec(execAsPipeline = false): Promise> { - if (execAsPipeline) { - return this.execAsPipeline(); - } - - return this.#multi.handleExecReplies( - await this.#executor(this.#multi.queue, this.#firstKey, RedisMultiCommand.generateChainId()) - ); - } - - EXEC = this.exec; - - async execAsPipeline(): Promise> { - return this.#multi.transformReplies( - await this.#executor(this.#multi.queue, this.#firstKey) - ); - } -} - -attachCommands({ - BaseClass: RedisClusterMultiCommand, - commands: COMMANDS, - executor: RedisClusterMultiCommand.prototype.commandsExecutor -}); +// import COMMANDS from './commands'; +// import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, ExcludeMappedString, RedisFunction } from '../commands'; +// import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; +// import { attachCommands, attachExtensions } from '../commander'; +// import RedisCluster from '.'; + +// type RedisClusterMultiCommandSignature< +// C extends RedisCommand, +// M extends RedisModules, +// F extends RedisFunctions, +// S extends RedisScripts +// > = (...args: Parameters) => RedisClusterMultiCommandType; + +// type WithCommands< +// M extends RedisModules, +// F extends RedisFunctions, +// S extends RedisScripts +// > = { +// [P in keyof typeof COMMANDS]: RedisClusterMultiCommandSignature<(typeof COMMANDS)[P], M, F, S>; +// }; + +// type WithModules< +// M extends RedisModules, +// F extends RedisFunctions, +// S extends RedisScripts +// > = { +// [P in keyof M as ExcludeMappedString

]: { +// [C in keyof M[P] as ExcludeMappedString]: RedisClusterMultiCommandSignature; +// }; +// }; + +// type WithFunctions< +// M extends RedisModules, +// F extends RedisFunctions, +// S extends RedisScripts +// > = { +// [P in keyof F as ExcludeMappedString

]: { +// [FF in keyof F[P] as ExcludeMappedString]: RedisClusterMultiCommandSignature; +// }; +// }; + +// type WithScripts< +// M extends RedisModules, +// F extends RedisFunctions, +// S extends RedisScripts +// > = { +// [P in keyof S as ExcludeMappedString

]: RedisClusterMultiCommandSignature; +// }; + +// export type RedisClusterMultiCommandType< +// M extends RedisModules, +// F extends RedisFunctions, +// S extends RedisScripts +// > = RedisClusterMultiCommand & WithCommands & WithModules & WithFunctions & WithScripts; + +// export type InstantiableRedisClusterMultiCommandType< +// M extends RedisModules, +// F extends RedisFunctions, +// S extends RedisScripts +// > = new (...args: ConstructorParameters) => RedisClusterMultiCommandType; + +// export type RedisClusterMultiExecutor = (queue: Array, firstKey?: RedisCommandArgument, chainId?: symbol) => Promise>; + +// export default class RedisClusterMultiCommand { +// readonly #multi = new RedisMultiCommand(); +// readonly #executor: RedisClusterMultiExecutor; +// #firstKey: RedisCommandArgument | undefined; + +// static extend< +// M extends RedisModules, +// F extends RedisFunctions, +// S extends RedisScripts +// >(extensions?: RedisExtensions): InstantiableRedisClusterMultiCommandType { +// return attachExtensions({ +// BaseClass: RedisClusterMultiCommand, +// modulesExecutor: RedisClusterMultiCommand.prototype.commandsExecutor, +// modules: extensions?.modules, +// functionsExecutor: RedisClusterMultiCommand.prototype.functionsExecutor, +// functions: extensions?.functions, +// scriptsExecutor: RedisClusterMultiCommand.prototype.scriptsExecutor, +// scripts: extensions?.scripts +// }); +// } + +// constructor(executor: RedisClusterMultiExecutor, firstKey?: RedisCommandArgument) { +// this.#executor = executor; +// this.#firstKey = firstKey; +// } + +// commandsExecutor(command: RedisCommand, args: Array): this { +// const transformedArguments = command.transformArguments(...args); +// this.#firstKey ??= RedisCluster.extractFirstKey(command, args, transformedArguments); +// return this.addCommand(undefined, transformedArguments, command.transformReply); +// } + +// addCommand( +// firstKey: RedisCommandArgument | undefined, +// args: RedisCommandArguments, +// transformReply?: RedisCommand['transformReply'] +// ): this { +// this.#firstKey ??= firstKey; +// this.#multi.addCommand(args, transformReply); +// return this; +// } + +// functionsExecutor(fn: RedisFunction, args: Array, name: string): this { +// const transformedArguments = this.#multi.addFunction(name, fn, args); +// this.#firstKey ??= RedisCluster.extractFirstKey(fn, args, transformedArguments); +// return this; +// } + +// scriptsExecutor(script: RedisScript, args: Array): this { +// const transformedArguments = this.#multi.addScript(script, args); +// this.#firstKey ??= RedisCluster.extractFirstKey(script, args, transformedArguments); +// return this; +// } + +// async exec(execAsPipeline = false): Promise> { +// if (execAsPipeline) { +// return this.execAsPipeline(); +// } + +// return this.#multi.handleExecReplies( +// await this.#executor(this.#multi.queue, this.#firstKey, RedisMultiCommand.generateChainId()) +// ); +// } + +// EXEC = this.exec; + +// async execAsPipeline(): Promise> { +// return this.#multi.transformReplies( +// await this.#executor(this.#multi.queue, this.#firstKey) +// ); +// } +// } + +// attachCommands({ +// BaseClass: RedisClusterMultiCommand, +// commands: COMMANDS, +// executor: RedisClusterMultiCommand.prototype.commandsExecutor +// }); diff --git a/packages/client/lib/command-options.ts b/packages/client/lib/command-options.ts deleted file mode 100644 index 8f91130b557..00000000000 --- a/packages/client/lib/command-options.ts +++ /dev/null @@ -1,14 +0,0 @@ -const symbol = Symbol('Command Options'); - -export type CommandOptions = T & { - readonly [symbol]: true; -}; - -export function commandOptions(options: T): CommandOptions { - (options as any)[symbol] = true; - return options as CommandOptions; -} - -export function isCommandOptions(options: any): options is CommandOptions { - return options?.[symbol] === true; -} diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index c04f41e1eb0..b1db7e701f4 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -1,165 +1,115 @@ - -import { ClientCommandOptions } from './client'; -import { CommandOptions, isCommandOptions } from './command-options'; -import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandReply, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts } from './commands'; - -type Instantiable = new (...args: Array) => T; - -type CommandsExecutor = - (command: C, args: Array, name: string) => unknown; - -interface AttachCommandsConfig { - BaseClass: Instantiable; - commands: Record; - executor: CommandsExecutor; +import { Command, CommanderConfig, RedisCommands, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, RespVersions } from './RESP/types'; + +interface AttachConfigOptions< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> { + BaseClass: new (...args: any) => any; + commands: RedisCommands; + createCommand(command: Command, resp: RespVersions): (...args: any) => any; + createModuleCommand(command: Command, resp: RespVersions): (...args: any) => any; + createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions): (...args: any) => any; + createScriptCommand(script: RedisScript, resp: RespVersions): (...args: any) => any; + config?: CommanderConfig; } -export function attachCommands({ - BaseClass, - commands, - executor -}: AttachCommandsConfig): void { - for (const [name, command] of Object.entries(commands)) { - BaseClass.prototype[name] = function (...args: Array): unknown { - return executor.call(this, command, args, name); - }; +export function attachConfig< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +>({ + BaseClass, + commands, + createCommand, + createModuleCommand, + createFunctionCommand, + createScriptCommand, + config +}: AttachConfigOptions) { + const RESP = config?.RESP ?? 2, + Class: any = class extends BaseClass {}; + + for (const [name, command] of Object.entries(commands)) { + Class.prototype[name] = createCommand(command, RESP); + } + + if (config?.modules) { + for (const [moduleName, module] of Object.entries(config.modules)) { + const fns = Object.create(null); + for (const [name, command] of Object.entries(module)) { + fns[name] = createModuleCommand(command, RESP); + } + + attachNamespace(Class.prototype, moduleName, fns); } -} - -interface AttachExtensionsConfig { - BaseClass: T; - modulesExecutor: CommandsExecutor; - modules?: RedisModules; - functionsExecutor: CommandsExecutor; - functions?: RedisFunctions; - scriptsExecutor: CommandsExecutor; - scripts?: RedisScripts; -} - -export function attachExtensions(config: AttachExtensionsConfig): any { - let Commander; + } - if (config.modules) { - Commander = attachWithNamespaces({ - BaseClass: config.BaseClass, - namespaces: config.modules, - executor: config.modulesExecutor - }); - } + if (config?.functions) { + for (const [library, commands] of Object.entries(config.functions)) { + const fns = Object.create(null); + for (const [name, command] of Object.entries(commands)) { + fns[name] = createFunctionCommand(name, command, RESP); + } - if (config.functions) { - Commander = attachWithNamespaces({ - BaseClass: Commander ?? config.BaseClass, - namespaces: config.functions, - executor: config.functionsExecutor - }); + attachNamespace(Class.prototype, library, fns); } + } - if (config.scripts) { - Commander ??= class extends config.BaseClass {}; - attachCommands({ - BaseClass: Commander, - commands: config.scripts, - executor: config.scriptsExecutor - }); + if (config?.scripts) { + for (const [name, script] of Object.entries(config.scripts)) { + Class.prototype[name] = createScriptCommand(script, RESP); } + } - return Commander ?? config.BaseClass; -} - -interface AttachWithNamespacesConfig { - BaseClass: Instantiable; - namespaces: Record>; - executor: CommandsExecutor; + return Class; } -function attachWithNamespaces({ - BaseClass, - namespaces, - executor -}: AttachWithNamespacesConfig): any { - const Commander = class extends BaseClass { - constructor(...args: Array) { - super(...args); - - for (const namespace of Object.keys(namespaces)) { - this[namespace] = Object.create(this[namespace], { - self: { - value: this - } - }); - } - } - }; - - for (const [namespace, commands] of Object.entries(namespaces)) { - Commander.prototype[namespace] = {}; - for (const [name, command] of Object.entries(commands)) { - Commander.prototype[namespace][name] = function (...args: Array): unknown { - return executor.call(this.self, command, args, name); - }; - } +function attachNamespace(prototype: any, name: PropertyKey, fns: any) { + Object.defineProperty(prototype, name, { + get() { + const value = Object.create(fns); + value.self = this; + Object.defineProperty(this, name, { value }); + return value; } - - return Commander; + }); } -export function transformCommandArguments( - command: RedisCommand, - args: Array -): { - jsArgs: Array; - args: RedisCommandArguments; - options: CommandOptions | undefined; -} { - let options; - if (isCommandOptions(args[0])) { - options = args[0]; - args = args.slice(1); - } +export function getTransformReply(command: Command, resp: RespVersions) { + switch (typeof command.transformReply) { + case 'function': + return command.transformReply; - return { - jsArgs: args, - args: command.transformArguments(...args), - options - }; + case 'object': + return command.transformReply[resp]; + } } -export function transformLegacyCommandArguments(args: Array): Array { - return args.flat().map(arg => { - return typeof arg === 'number' || arg instanceof Date ? - arg.toString() : - arg; - }); -} +export function functionArgumentsPrefix(name: string, fn: RedisFunction) { + const prefix: Array = [ + fn.IS_READ_ONLY ? 'FCALL_RO' : 'FCALL', + name + ]; -export function transformCommandReply( - command: C, - rawReply: unknown, - preserved: unknown -): RedisCommandReply { - if (!command.transformReply) { - return rawReply as RedisCommandReply; - } + if (fn.NUMBER_OF_KEYS !== undefined) { + prefix.push(fn.NUMBER_OF_KEYS.toString()); + } - return command.transformReply(rawReply, preserved); + return prefix; } -export function fCallArguments( - name: RedisCommandArgument, - fn: RedisFunction, - args: RedisCommandArguments -): RedisCommandArguments { - const actualArgs: RedisCommandArguments = [ - fn.IS_READ_ONLY ? 'FCALL_RO' : 'FCALL', - name - ]; - - if (fn.NUMBER_OF_KEYS !== undefined) { - actualArgs.push(fn.NUMBER_OF_KEYS.toString()); - } +export function scriptArgumentsPrefix(script: RedisScript) { + const prefix: Array = [ + script.IS_READ_ONLY ? 'EVALSHA_RO' : 'EVALSHA', + script.SHA1 + ]; - actualArgs.push(...args); + if (script.NUMBER_OF_KEYS !== undefined) { + prefix.push(script.NUMBER_OF_KEYS.toString()); + } - return actualArgs; + return prefix; } diff --git a/packages/client/lib/commands/ACL_CAT.spec.ts b/packages/client/lib/commands/ACL_CAT.spec.ts index 521871a1c6b..0fc8b08b2fa 100644 --- a/packages/client/lib/commands/ACL_CAT.spec.ts +++ b/packages/client/lib/commands/ACL_CAT.spec.ts @@ -1,23 +1,31 @@ import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_CAT'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_CAT from './ACL_CAT'; describe('ACL CAT', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'CAT'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ACL_CAT.transformArguments(), + ['ACL', 'CAT'] + ); + }); - it('with categoryName', () => { - assert.deepEqual( - transformArguments('dangerous'), - ['ACL', 'CAT', 'dangerous'] - ); - }); + it('with categoryName', () => { + assert.deepEqual( + ACL_CAT.transformArguments('dangerous'), + ['ACL', 'CAT', 'dangerous'] + ); }); + }); + + testUtils.testWithClient('client.aclCat', async client => { + const categories = await client.aclCat(); + assert.ok(Array.isArray(categories)); + for (const category of categories) { + assert.equal(typeof category, 'string'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_CAT.ts b/packages/client/lib/commands/ACL_CAT.ts index 161546cfbe9..dd4762239a7 100644 --- a/packages/client/lib/commands/ACL_CAT.ts +++ b/packages/client/lib/commands/ACL_CAT.ts @@ -1,13 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(categoryName?: RedisCommandArgument): RedisCommandArguments { - const args: RedisCommandArguments = ['ACL', 'CAT']; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(categoryName?: RedisArgument) { + const args: Array = ['ACL', 'CAT']; if (categoryName) { - args.push(categoryName); + args.push(categoryName); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_DELUSER.spec.ts b/packages/client/lib/commands/ACL_DELUSER.spec.ts index 5c5ea2fa2a3..bccd2ccdc5e 100644 --- a/packages/client/lib/commands/ACL_DELUSER.spec.ts +++ b/packages/client/lib/commands/ACL_DELUSER.spec.ts @@ -1,30 +1,30 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ACL_DELUSER'; +import ACL_DELUSER from './ACL_DELUSER'; describe('ACL DELUSER', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('username'), - ['ACL', 'DELUSER', 'username'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ACL_DELUSER.transformArguments('username'), + ['ACL', 'DELUSER', 'username'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ACL', 'DELUSER', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + ACL_DELUSER.transformArguments(['1', '2']), + ['ACL', 'DELUSER', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.aclDelUser', async client => { - assert.equal( - await client.aclDelUser('dosenotexists'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.aclDelUser', async client => { + assert.equal( + typeof await client.aclDelUser('user'), + 'number' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_DELUSER.ts b/packages/client/lib/commands/ACL_DELUSER.ts index 25ed1a10300..c0f8e15d672 100644 --- a/packages/client/lib/commands/ACL_DELUSER.ts +++ b/packages/client/lib/commands/ACL_DELUSER.ts @@ -1,10 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export function transformArguments( - username: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['ACL', 'DELUSER'], username); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(username: RedisVariadicArgument) { + return pushVariadicArguments(['ACL', 'DELUSER'], username); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_DRYRUN.spec.ts b/packages/client/lib/commands/ACL_DRYRUN.spec.ts index 3154689c29e..051e6a561da 100644 --- a/packages/client/lib/commands/ACL_DRYRUN.spec.ts +++ b/packages/client/lib/commands/ACL_DRYRUN.spec.ts @@ -1,21 +1,21 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ACL_DRYRUN'; +import ACL_DRYRUN from './ACL_DRYRUN'; describe('ACL DRYRUN', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('default', ['GET', 'key']), - ['ACL', 'DRYRUN', 'default', 'GET', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_DRYRUN.transformArguments('default', ['GET', 'key']), + ['ACL', 'DRYRUN', 'default', 'GET', 'key'] + ); + }); - testUtils.testWithClient('client.aclDryRun', async client => { - assert.equal( - await client.aclDryRun('default', ['GET', 'key']), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.aclDryRun', async client => { + assert.equal( + await client.aclDryRun('default', ['GET', 'key']), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_DRYRUN.ts b/packages/client/lib/commands/ACL_DRYRUN.ts index 95eed95066c..257f0fe61e2 100644 --- a/packages/client/lib/commands/ACL_DRYRUN.ts +++ b/packages/client/lib/commands/ACL_DRYRUN.ts @@ -1,18 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments( - username: RedisCommandArgument, - command: Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(username: RedisArgument, command: Array) { return [ - 'ACL', - 'DRYRUN', - username, - ...command + 'ACL', + 'DRYRUN', + username, + ...command ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> | BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_GENPASS.spec.ts b/packages/client/lib/commands/ACL_GENPASS.spec.ts index 3b2a022f972..7b489f1e9ec 100644 --- a/packages/client/lib/commands/ACL_GENPASS.spec.ts +++ b/packages/client/lib/commands/ACL_GENPASS.spec.ts @@ -1,23 +1,30 @@ import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_GENPASS'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_GENPASS from './ACL_GENPASS'; describe('ACL GENPASS', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'GENPASS'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ACL_GENPASS.transformArguments(), + ['ACL', 'GENPASS'] + ); + }); - it('with bits', () => { - assert.deepEqual( - transformArguments(128), - ['ACL', 'GENPASS', '128'] - ); - }); + it('with bits', () => { + assert.deepEqual( + ACL_GENPASS.transformArguments(128), + ['ACL', 'GENPASS', '128'] + ); }); + }); + + testUtils.testWithClient('client.aclGenPass', async client => { + assert.equal( + typeof await client.aclGenPass(), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_GENPASS.ts b/packages/client/lib/commands/ACL_GENPASS.ts index 91a71e220e0..be89ff90a9a 100644 --- a/packages/client/lib/commands/ACL_GENPASS.ts +++ b/packages/client/lib/commands/ACL_GENPASS.ts @@ -1,13 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(bits?: number): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(bits?: number) { const args = ['ACL', 'GENPASS']; if (bits) { - args.push(bits.toString()); + args.push(bits.toString()); } return args; -} + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; -export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/ACL_GETUSER.spec.ts b/packages/client/lib/commands/ACL_GETUSER.spec.ts index f91f2ff9e5c..6a0948d6f07 100644 --- a/packages/client/lib/commands/ACL_GETUSER.spec.ts +++ b/packages/client/lib/commands/ACL_GETUSER.spec.ts @@ -1,44 +1,34 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ACL_GETUSER'; +import ACL_GETUSER from './ACL_GETUSER'; describe('ACL GETUSER', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('username'), - ['ACL', 'GETUSER', 'username'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_GETUSER.transformArguments('username'), + ['ACL', 'GETUSER', 'username'] + ); + }); - testUtils.testWithClient('client.aclGetUser', async client => { - const expectedReply: any = { - passwords: [], - commands: '+@all', - }; + testUtils.testWithClient('client.aclGetUser', async client => { + const reply = await client.aclGetUser('default'); - if (testUtils.isVersionGreaterThan([7])) { - expectedReply.flags = ['on', 'nopass']; - expectedReply.keys = '~*'; - expectedReply.channels = '&*'; - expectedReply.selectors = []; - } else { - expectedReply.keys = ['*']; - expectedReply.selectors = undefined; + assert.ok(Array.isArray(reply.passwords)); + assert.equal(typeof reply.commands, 'string'); + assert.ok(Array.isArray(reply.flags)); - if (testUtils.isVersionGreaterThan([6, 2])) { - expectedReply.flags = ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass']; - expectedReply.channels = ['*']; - } else { - expectedReply.flags = ['on', 'allkeys', 'allcommands', 'nopass']; - expectedReply.channels = undefined; - } - } + if (testUtils.isVersionGreaterThan([7])) { + assert.equal(typeof reply.keys, 'string'); + assert.equal(typeof reply.channels, 'string'); + assert.ok(Array.isArray(reply.selectors)); + } else { + assert.ok(Array.isArray(reply.keys)); - assert.deepEqual( - await client.aclGetUser('default'), - expectedReply - ); - }, GLOBAL.SERVERS.OPEN); + if (testUtils.isVersionGreaterThan([6, 2])) { + assert.ok(Array.isArray(reply.channels)); + } + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_GETUSER.ts b/packages/client/lib/commands/ACL_GETUSER.ts index 818a945bac1..c9489abaf51 100644 --- a/packages/client/lib/commands/ACL_GETUSER.ts +++ b/packages/client/lib/commands/ACL_GETUSER.ts @@ -1,40 +1,40 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, Resp2Reply, Command } from '../RESP/types'; -export function transformArguments(username: RedisCommandArgument): RedisCommandArguments { - return ['ACL', 'GETUSER', username]; -} - -type AclGetUserRawReply = [ - 'flags', - Array, - 'passwords', - Array, - 'commands', - RedisCommandArgument, - 'keys', - Array | RedisCommandArgument, - 'channels', - Array | RedisCommandArgument, - 'selectors' | undefined, - Array> | undefined -]; +type AclUser = TuplesToMapReply<[ + [BlobStringReply<'flags'>, ArrayReply], + [BlobStringReply<'passwords'>, ArrayReply], + [BlobStringReply<'commands'>, BlobStringReply], + /** changed to BlobStringReply in 7.0 */ + [BlobStringReply<'keys'>, ArrayReply | BlobStringReply], + /** added in 6.2, changed to BlobStringReply in 7.0 */ + [BlobStringReply<'channels'>, ArrayReply | BlobStringReply], + /** added in 7.0 */ + [BlobStringReply<'selectors'>, ArrayReply, BlobStringReply], + [BlobStringReply<'keys'>, BlobStringReply], + [BlobStringReply<'channels'>, BlobStringReply] + ]>>], +]>; -interface AclUser { - flags: Array; - passwords: Array; - commands: RedisCommandArgument; - keys: Array | RedisCommandArgument; - channels: Array | RedisCommandArgument; - selectors?: Array>; -} - -export function transformReply(reply: AclGetUserRawReply): AclUser { - return { - flags: reply[1], - passwords: reply[3], - commands: reply[5], - keys: reply[7], - channels: reply[9], - selectors: reply[11] - }; -} +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(username: RedisArgument) { + return ['ACL', 'GETUSER', username]; + }, + transformReply: { + 2: (reply: Resp2Reply) => ({ + flags: reply[1], + passwords: reply[3], + commands: reply[5], + keys: reply[7], + channels: reply[9], + selectors: reply[11]?.map(selector => ({ + commands: selector[1], + keys: selector[3], + channels: selector[5] + })) + }), + 3: undefined as unknown as () => AclUser + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LIST.spec.ts b/packages/client/lib/commands/ACL_LIST.spec.ts index 9f9156db7a2..8b2d875bda1 100644 --- a/packages/client/lib/commands/ACL_LIST.spec.ts +++ b/packages/client/lib/commands/ACL_LIST.spec.ts @@ -1,14 +1,22 @@ import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_LIST'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_LIST from './ACL_LIST'; describe('ACL LIST', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LIST'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_LIST.transformArguments(), + ['ACL', 'LIST'] + ); + }); + + testUtils.testWithClient('client.aclList', async client => { + const users = await client.aclList(); + assert.ok(Array.isArray(users)); + for (const user of users) { + assert.equal(typeof user, 'string'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_LIST.ts b/packages/client/lib/commands/ACL_LIST.ts index ae523fe9ce9..1a831a4987c 100644 --- a/packages/client/lib/commands/ACL_LIST.ts +++ b/packages/client/lib/commands/ACL_LIST.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'LIST']; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LOAD.spec.ts b/packages/client/lib/commands/ACL_LOAD.spec.ts index 703d5eeb252..66afcf356a7 100644 --- a/packages/client/lib/commands/ACL_LOAD.spec.ts +++ b/packages/client/lib/commands/ACL_LOAD.spec.ts @@ -1,14 +1,14 @@ import { strict as assert } from 'assert'; import testUtils from '../test-utils'; -import { transformArguments } from './ACL_SAVE'; +import ACL_LOAD from './ACL_LOAD'; -describe('ACL SAVE', () => { - testUtils.isVersionGreaterThanHook([6]); +describe('ACL LOAD', () => { + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'SAVE'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_LOAD.transformArguments(), + ['ACL', 'LOAD'] + ); + }); }); diff --git a/packages/client/lib/commands/ACL_LOAD.ts b/packages/client/lib/commands/ACL_LOAD.ts index 88309102b95..39587829b17 100644 --- a/packages/client/lib/commands/ACL_LOAD.ts +++ b/packages/client/lib/commands/ACL_LOAD.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'LOAD']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LOG.spec.ts b/packages/client/lib/commands/ACL_LOG.spec.ts index a8296d31da6..6b63b59f81c 100644 --- a/packages/client/lib/commands/ACL_LOG.spec.ts +++ b/packages/client/lib/commands/ACL_LOG.spec.ts @@ -1,53 +1,53 @@ import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments, transformReply } from './ACL_LOG'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_LOG from './ACL_LOG'; describe('ACL LOG', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LOG'] - ); - }); - - it('with count', () => { - assert.deepEqual( - transformArguments(10), - ['ACL', 'LOG', '10'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ACL_LOG.transformArguments(), + ['ACL', 'LOG'] + ); }); - it('transformReply', () => { - assert.deepEqual( - transformReply([[ - 'count', - 1, - 'reason', - 'auth', - 'context', - 'toplevel', - 'object', - 'AUTH', - 'username', - 'someuser', - 'age-seconds', - '4.096', - 'client-info', - 'id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default' - ]]), - [{ - count: 1, - reason: 'auth', - context: 'toplevel', - object: 'AUTH', - username: 'someuser', - ageSeconds: 4.096, - clientInfo: 'id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default' - }] - ); + it('with count', () => { + assert.deepEqual( + ACL_LOG.transformArguments(10), + ['ACL', 'LOG', '10'] + ); }); + }); + + testUtils.testWithClient('client.aclLog', async client => { + // make sure to create at least one log + await Promise.all([ + client.aclSetUser('test', 'on >test'), + client.auth({ + username: 'test', + password: 'test' + }), + client.auth({ + username: 'default', + password: '' + }) + ]); + + const logs = await client.aclLog(); + assert.ok(Array.isArray(logs)); + for (const log of logs) { + + assert.equal(typeof log.count, 'number'); + assert.equal(typeof log.timestamp, 'number'); + assert.equal(typeof log.username, 'string'); + assert.equal(typeof log.clientId, 'string'); + assert.equal(typeof log.command, 'string'); + assert.equal(typeof log.args, 'string'); + assert.equal(typeof log.key, 'string'); + assert.equal(typeof log.result, 'number'); + assert.equal(typeof log.duration, 'number'); + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_LOG.ts b/packages/client/lib/commands/ACL_LOG.ts index 0fd9aa6f19d..4140111ecfc 100644 --- a/packages/client/lib/commands/ACL_LOG.ts +++ b/packages/client/lib/commands/ACL_LOG.ts @@ -1,50 +1,38 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { Resp2Reply } from '../RESP/types'; +import { ArrayReply, BlobStringReply, Command, NumberReply, TuplesToMapReply } from '../RESP/types'; -export function transformArguments(count?: number): RedisCommandArguments { +export type AclLogReply = ArrayReply, NumberReply], + [BlobStringReply<'reason'>, BlobStringReply], + [BlobStringReply<'context'>, BlobStringReply], + [BlobStringReply<'object'>, BlobStringReply], + [BlobStringReply<'username'>, BlobStringReply], + [BlobStringReply<'age-seconds'>, BlobStringReply], + [BlobStringReply<'client-info'>, BlobStringReply] +]>>; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(count?: number) { const args = ['ACL', 'LOG']; - if (count) { - args.push(count.toString()); + if (count !== undefined) { + args.push(count.toString()); } return args; -} - -type AclLogRawReply = [ - _: RedisCommandArgument, - count: number, - _: RedisCommandArgument, - reason: RedisCommandArgument, - _: RedisCommandArgument, - context: RedisCommandArgument, - _: RedisCommandArgument, - object: RedisCommandArgument, - _: RedisCommandArgument, - username: RedisCommandArgument, - _: RedisCommandArgument, - ageSeconds: RedisCommandArgument, - _: RedisCommandArgument, - clientInfo: RedisCommandArgument -]; - -interface AclLog { - count: number; - reason: RedisCommandArgument; - context: RedisCommandArgument; - object: RedisCommandArgument; - username: RedisCommandArgument; - ageSeconds: number; - clientInfo: RedisCommandArgument; -} - -export function transformReply(reply: Array): Array { - return reply.map(log => ({ - count: log[1], - reason: log[3], - context: log[5], - object: log[7], - username: log[9], - ageSeconds: Number(log[11]), - clientInfo: log[13] - })); -} + }, + transformReply: { + 2: (reply: Resp2Reply) => ({ + count: Number(reply[1]), + reason: reply[3], + context: reply[5], + object: reply[7], + username: reply[9], + 'age-seconds': Number(reply[11]), + 'client-info': reply[13] + }), + 3: undefined as unknown as () => AclLogReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_LOG_RESET.spec.ts b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts index 5d26e45d04f..7f3fbf1ade9 100644 --- a/packages/client/lib/commands/ACL_LOG_RESET.spec.ts +++ b/packages/client/lib/commands/ACL_LOG_RESET.spec.ts @@ -1,14 +1,21 @@ import { strict as assert } from 'assert'; -import testUtils from '../test-utils'; -import { transformArguments } from './ACL_LOG_RESET'; +import testUtils, { GLOBAL } from '../test-utils'; +import ACL_LOG_RESET from './ACL_LOG_RESET'; describe('ACL LOG RESET', () => { - testUtils.isVersionGreaterThanHook([6]); + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LOG', 'RESET'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_LOG_RESET.transformArguments(), + ['ACL', 'LOG', 'RESET'] + ); + }); + + testUtils.testWithClient('client.aclLogReset', async client => { + assert.equal( + await client.aclLogReset(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ACL_LOG_RESET.ts b/packages/client/lib/commands/ACL_LOG_RESET.ts index 8ff0be4f8b9..91d58d538e9 100644 --- a/packages/client/lib/commands/ACL_LOG_RESET.ts +++ b/packages/client/lib/commands/ACL_LOG_RESET.ts @@ -1,7 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; +import ACL_LOG from './ACL_LOG'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: ACL_LOG.IS_READ_ONLY, + transformArguments() { return ['ACL', 'LOG', 'RESET']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_SAVE.spec.ts b/packages/client/lib/commands/ACL_SAVE.spec.ts index f4de312bb7a..065b75f93b6 100644 --- a/packages/client/lib/commands/ACL_SAVE.spec.ts +++ b/packages/client/lib/commands/ACL_SAVE.spec.ts @@ -1,14 +1,14 @@ import { strict as assert } from 'assert'; import testUtils from '../test-utils'; -import { transformArguments } from './ACL_LOAD'; +import ACL_SAVE from './ACL_SAVE'; -describe('ACL LOAD', () => { - testUtils.isVersionGreaterThanHook([6]); +describe('ACL SAVE', () => { + testUtils.isVersionGreaterThanHook([6]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['ACL', 'LOAD'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ACL_SAVE.transformArguments(), + ['ACL', 'SAVE'] + ); + }); }); diff --git a/packages/client/lib/commands/ACL_SAVE.ts b/packages/client/lib/commands/ACL_SAVE.ts index e57cd697297..8c2e2dab115 100644 --- a/packages/client/lib/commands/ACL_SAVE.ts +++ b/packages/client/lib/commands/ACL_SAVE.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'SAVE']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_SETUSER.ts b/packages/client/lib/commands/ACL_SETUSER.ts index a12cc8ed24e..c99fec3d9ba 100644 --- a/packages/client/lib/commands/ACL_SETUSER.ts +++ b/packages/client/lib/commands/ACL_SETUSER.ts @@ -1,11 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export function transformArguments( - username: RedisCommandArgument, - rule: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['ACL', 'SETUSER', username], rule); -} - -export declare function transformReply(): RedisCommandArgument; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(username: RedisArgument, rule: RedisVariadicArgument) { + return pushVariadicArguments(['ACL', 'SETUSER', username], rule); + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_USERS.ts b/packages/client/lib/commands/ACL_USERS.ts index 7970a262e26..ee8c619f25f 100644 --- a/packages/client/lib/commands/ACL_USERS.ts +++ b/packages/client/lib/commands/ACL_USERS.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ACL', 'USERS']; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ACL_WHOAMI.ts b/packages/client/lib/commands/ACL_WHOAMI.ts index 3c41171638e..5aa56c4d351 100644 --- a/packages/client/lib/commands/ACL_WHOAMI.ts +++ b/packages/client/lib/commands/ACL_WHOAMI.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['ACL', 'WHOAMI']; -} - -export declare function transformReply(): RedisCommandArgument; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['ACL', 'USERS']; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/APPEND.spec.ts b/packages/client/lib/commands/APPEND.spec.ts index 23353866843..c85501f4a0c 100644 --- a/packages/client/lib/commands/APPEND.spec.ts +++ b/packages/client/lib/commands/APPEND.spec.ts @@ -1,11 +1,22 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './APPEND'; +import testUtils, { GLOBAL } from '../test-utils'; +import APPEND from './APPEND'; describe('APPEND', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'value'), - ['APPEND', 'key', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + APPEND.transformArguments('key', 'value'), + ['APPEND', 'key', 'value'] + ); + }); + + testUtils.testAll('append', async client => { + assert.equal( + await client.append('key', 'value'), + 5 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/APPEND.ts b/packages/client/lib/commands/APPEND.ts index 66f7fc84798..1bc01024998 100644 --- a/packages/client/lib/commands/APPEND.ts +++ b/packages/client/lib/commands/APPEND.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, value: RedisArgument) { return ['APPEND', key, value]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ASKING.ts b/packages/client/lib/commands/ASKING.ts index 8a87806fe62..c6ada477ee4 100644 --- a/packages/client/lib/commands/ASKING.ts +++ b/packages/client/lib/commands/ASKING.ts @@ -1,7 +1,10 @@ -import { RedisCommandArguments, RedisCommandArgument } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['ASKING']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/AUTH.spec.ts b/packages/client/lib/commands/AUTH.spec.ts index 1907488346e..290ba936bf7 100644 --- a/packages/client/lib/commands/AUTH.spec.ts +++ b/packages/client/lib/commands/AUTH.spec.ts @@ -1,25 +1,25 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './AUTH'; +import AUTH from './AUTH'; describe('AUTH', () => { - describe('transformArguments', () => { - it('password only', () => { - assert.deepEqual( - transformArguments({ - password: 'password' - }), - ['AUTH', 'password'] - ); - }); + describe('transformArguments', () => { + it('password only', () => { + assert.deepEqual( + AUTH.transformArguments({ + password: 'password' + }), + ['AUTH', 'password'] + ); + }); - it('username & password', () => { - assert.deepEqual( - transformArguments({ - username: 'username', - password: 'password' - }), - ['AUTH', 'username', 'password'] - ); - }); + it('username & password', () => { + assert.deepEqual( + AUTH.transformArguments({ + username: 'username', + password: 'password' + }), + ['AUTH', 'username', 'password'] + ); }); + }); }); diff --git a/packages/client/lib/commands/AUTH.ts b/packages/client/lib/commands/AUTH.ts index 49b0df6d313..4c7a0b0c765 100644 --- a/packages/client/lib/commands/AUTH.ts +++ b/packages/client/lib/commands/AUTH.ts @@ -1,16 +1,23 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export interface AuthOptions { - username?: RedisCommandArgument; - password: RedisCommandArgument; + username?: RedisArgument; + password: RedisArgument; } -export function transformArguments({ username, password }: AuthOptions): RedisCommandArguments { - if (!username) { - return ['AUTH', password]; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments({ username, password }: AuthOptions) { + const args: Array = ['AUTH']; + + if (username !== undefined) { + args.push(username); } - return ['AUTH', username, password]; -} + args.push(password); -export declare function transformReply(): RedisCommandArgument; + return args; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/BGREWRITEAOF.ts b/packages/client/lib/commands/BGREWRITEAOF.ts index be4ec2546ab..5f9a46e318f 100644 --- a/packages/client/lib/commands/BGREWRITEAOF.ts +++ b/packages/client/lib/commands/BGREWRITEAOF.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['BGREWRITEAOF']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BGSAVE.ts b/packages/client/lib/commands/BGSAVE.ts index 9c90f3485be..1cc60cb5dae 100644 --- a/packages/client/lib/commands/BGSAVE.ts +++ b/packages/client/lib/commands/BGSAVE.ts @@ -1,17 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -interface BgSaveOptions { - SCHEDULE?: true; -} - -export function transformArguments(options?: BgSaveOptions): RedisCommandArguments { - const args = ['BGSAVE']; - - if (options?.SCHEDULE) { - args.push('SCHEDULE'); - } - - return args; -} - -export declare function transformReply(): RedisCommandArgument; +import { SimpleStringReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['BGSAVE']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITCOUNT.ts b/packages/client/lib/commands/BITCOUNT.ts index 4bbd4f00911..6ec6b89be8b 100644 --- a/packages/client/lib/commands/BITCOUNT.ts +++ b/packages/client/lib/commands/BITCOUNT.ts @@ -1,33 +1,29 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface BitCountRange { - start: number; - end: number; - mode?: 'BYTE' | 'BIT'; +export interface BitCountRange { + start: number; + end: number; + mode?: 'BYTE' | 'BIT'; } -export function transformArguments( - key: RedisCommandArgument, - range?: BitCountRange -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, range?: BitCountRange) { const args = ['BITCOUNT', key]; if (range) { - args.push( - range.start.toString(), - range.end.toString() - ); - - if (range.mode) { - args.push(range.mode); - } + args.push( + range.start.toString(), + range.end.toString() + ); + + if (range.mode) { + args.push(range.mode); + } } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITFIELD.spec.ts b/packages/client/lib/commands/BITFIELD.spec.ts index aaf0f93e501..9b94b19d240 100644 --- a/packages/client/lib/commands/BITFIELD.spec.ts +++ b/packages/client/lib/commands/BITFIELD.spec.ts @@ -1,46 +1,49 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITFIELD'; +import BITFIELD from './BITFIELD'; describe('BITFIELD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [{ - operation: 'OVERFLOW', - behavior: 'WRAP' - }, { - operation: 'GET', - encoding: 'i8', - offset: 0 - }, { - operation: 'OVERFLOW', - behavior: 'SAT' - }, { - operation: 'SET', - encoding: 'i16', - offset: 1, - value: 0 - }, { - operation: 'OVERFLOW', - behavior: 'FAIL' - }, { - operation: 'INCRBY', - encoding: 'i32', - offset: 2, - increment: 1 - }]), - ['BITFIELD', 'key', 'OVERFLOW', 'WRAP', 'GET', 'i8', '0', 'OVERFLOW', 'SAT', 'SET', 'i16', '1', '0', 'OVERFLOW', 'FAIL', 'INCRBY', 'i32', '2', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BITFIELD.transformArguments('key', [{ + operation: 'OVERFLOW', + behavior: 'WRAP' + }, { + operation: 'GET', + encoding: 'i8', + offset: 0 + }, { + operation: 'OVERFLOW', + behavior: 'SAT' + }, { + operation: 'SET', + encoding: 'i16', + offset: 1, + value: 0 + }, { + operation: 'OVERFLOW', + behavior: 'FAIL' + }, { + operation: 'INCRBY', + encoding: 'i32', + offset: 2, + increment: 1 + }]), + ['BITFIELD', 'key', 'OVERFLOW', 'WRAP', 'GET', 'i8', '0', 'OVERFLOW', 'SAT', 'SET', 'i16', '1', '0', 'OVERFLOW', 'FAIL', 'INCRBY', 'i32', '2', '1'] + ); + }); - testUtils.testWithClient('client.bitField', async client => { - assert.deepEqual( - await client.bitField('key', [{ - operation: 'GET', - encoding: 'i8', - offset: 0 - }]), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('bitField', async client => { + assert.deepEqual( + await client.bitField('key', [{ + operation: 'GET', + encoding: 'i8', + offset: 0 + }]), + [0] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITFIELD.ts b/packages/client/lib/commands/BITFIELD.ts index 6a477b89f01..5d7d4bf7282 100644 --- a/packages/client/lib/commands/BITFIELD.ts +++ b/packages/client/lib/commands/BITFIELD.ts @@ -1,80 +1,87 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '../RESP/types'; export type BitFieldEncoding = `${'i' | 'u'}${number}`; export interface BitFieldOperation { - operation: S; + operation: S; } export interface BitFieldGetOperation extends BitFieldOperation<'GET'> { - encoding: BitFieldEncoding; - offset: number | string; + encoding: BitFieldEncoding; + offset: number | string; } -interface BitFieldSetOperation extends BitFieldOperation<'SET'> { - encoding: BitFieldEncoding; - offset: number | string; - value: number; +export interface BitFieldSetOperation extends BitFieldOperation<'SET'> { + encoding: BitFieldEncoding; + offset: number | string; + value: number; } -interface BitFieldIncrByOperation extends BitFieldOperation<'INCRBY'> { - encoding: BitFieldEncoding; - offset: number | string; - increment: number; +export interface BitFieldIncrByOperation extends BitFieldOperation<'INCRBY'> { + encoding: BitFieldEncoding; + offset: number | string; + increment: number; } -interface BitFieldOverflowOperation extends BitFieldOperation<'OVERFLOW'> { - behavior: string; +export interface BitFieldOverflowOperation extends BitFieldOperation<'OVERFLOW'> { + behavior: string; } -type BitFieldOperations = Array< - BitFieldGetOperation | - BitFieldSetOperation | - BitFieldIncrByOperation | - BitFieldOverflowOperation +export type BitFieldOperations = Array< + BitFieldGetOperation | + BitFieldSetOperation | + BitFieldIncrByOperation | + BitFieldOverflowOperation >; -export function transformArguments(key: string, operations: BitFieldOperations): Array { +export type BitFieldRoOperations = Array< + Omit +>; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, operations: BitFieldOperations) { const args = ['BITFIELD', key]; for (const options of operations) { - switch (options.operation) { - case 'GET': - args.push( - 'GET', - options.encoding, - options.offset.toString() - ); - break; + switch (options.operation) { + case 'GET': + args.push( + 'GET', + options.encoding, + options.offset.toString() + ); + break; - case 'SET': - args.push( - 'SET', - options.encoding, - options.offset.toString(), - options.value.toString() - ); - break; + case 'SET': + args.push( + 'SET', + options.encoding, + options.offset.toString(), + options.value.toString() + ); + break; - case 'INCRBY': - args.push( - 'INCRBY', - options.encoding, - options.offset.toString(), - options.increment.toString() - ); - break; + case 'INCRBY': + args.push( + 'INCRBY', + options.encoding, + options.offset.toString(), + options.increment.toString() + ); + break; - case 'OVERFLOW': - args.push( - 'OVERFLOW', - options.behavior - ); - break; - } + case 'OVERFLOW': + args.push( + 'OVERFLOW', + options.behavior + ); + break; + } } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITFIELD_RO.spec.ts b/packages/client/lib/commands/BITFIELD_RO.spec.ts index 98399d5f235..2857155a60f 100644 --- a/packages/client/lib/commands/BITFIELD_RO.spec.ts +++ b/packages/client/lib/commands/BITFIELD_RO.spec.ts @@ -1,27 +1,30 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITFIELD_RO'; +import BITFIELD_RO from './BITFIELD_RO'; -describe('BITFIELD RO', () => { - testUtils.isVersionGreaterThanHook([6, 2]); +describe('BITFIELD_RO', () => { + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', [{ - encoding: 'i8', - offset: 0 - }]), - ['BITFIELD_RO', 'key', 'GET', 'i8', '0'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + BITFIELD_RO.transformArguments('key', [{ + encoding: 'i8', + offset: 0 + }]), + ['BITFIELD_RO', 'key', 'GET', 'i8', '0'] + ); + }); - testUtils.testWithClient('client.bitFieldRo', async client => { - assert.deepEqual( - await client.bitFieldRo('key', [{ - encoding: 'i8', - offset: 0 - }]), - [0] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('bitFieldRo', async client => { + assert.deepEqual( + await client.bitFieldRo('key', [{ + encoding: 'i8', + offset: 0 + }]), + [0] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITFIELD_RO.ts b/packages/client/lib/commands/BITFIELD_RO.ts index efd4eac188e..99e500abc04 100644 --- a/packages/client/lib/commands/BITFIELD_RO.ts +++ b/packages/client/lib/commands/BITFIELD_RO.ts @@ -1,26 +1,25 @@ +import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; import { BitFieldGetOperation } from './BITFIELD'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -type BitFieldRoOperations = Array< - Omit & - Partial> +export type BitFieldRoOperations = Array< + Omit >; -export function transformArguments(key: string, operations: BitFieldRoOperations): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, operations: BitFieldRoOperations) { const args = ['BITFIELD_RO', key]; for (const operation of operations) { - args.push( - 'GET', - operation.encoding, - operation.offset.toString() - ); + args.push( + 'GET', + operation.encoding, + operation.offset.toString() + ); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITOP.spec.ts b/packages/client/lib/commands/BITOP.spec.ts index 554530d56f4..c89d5ea0b37 100644 --- a/packages/client/lib/commands/BITOP.spec.ts +++ b/packages/client/lib/commands/BITOP.spec.ts @@ -1,35 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './BITOP'; +import BITOP from './BITOP'; describe('BITOP', () => { - describe('transformArguments', () => { - it('single key', () => { - assert.deepEqual( - transformArguments('AND', 'destKey', 'key'), - ['BITOP', 'AND', 'destKey', 'key'] - ); - }); - - it('multiple keys', () => { - assert.deepEqual( - transformArguments('AND', 'destKey', ['1', '2']), - ['BITOP', 'AND', 'destKey', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('single key', () => { + assert.deepEqual( + BITOP.transformArguments('AND', 'destKey', 'key'), + ['BITOP', 'AND', 'destKey', 'key'] + ); }); - testUtils.testWithClient('client.bitOp', async client => { - assert.equal( - await client.bitOp('AND', 'destKey', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('multiple keys', () => { + assert.deepEqual( + BITOP.transformArguments('AND', 'destKey', ['1', '2']), + ['BITOP', 'AND', 'destKey', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.bitOp', async cluster => { - assert.equal( - await cluster.bitOp('AND', '{tag}destKey', '{tag}key'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('client.bitOp', async client => { + assert.equal( + await client.bitOp('AND', '{tag}destKey', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/BITOP.ts b/packages/client/lib/commands/BITOP.ts index e2953303d41..4c34845699e 100644 --- a/packages/client/lib/commands/BITOP.ts +++ b/packages/client/lib/commands/BITOP.ts @@ -1,16 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +export type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; -type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; - -export function transformArguments( +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments( operation: BitOperations, - destKey: RedisCommandArgument, - key: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['BITOP', operation, destKey], key); -} - -export declare function transformReply(): number; + destKey: RedisArgument, + key: RedisVariadicArgument + ) { + return pushVariadicArguments(['BITOP', operation, destKey], key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BITPOS.ts b/packages/client/lib/commands/BITPOS.ts index a9a035fd9f2..5f8a1031a45 100644 --- a/packages/client/lib/commands/BITPOS.ts +++ b/packages/client/lib/commands/BITPOS.ts @@ -1,32 +1,31 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { BitValue } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, bit: BitValue, start?: number, end?: number, mode?: 'BYTE' | 'BIT' -): RedisCommandArguments { + ) { const args = ['BITPOS', key, bit.toString()]; if (typeof start === 'number') { - args.push(start.toString()); + args.push(start.toString()); } if (typeof end === 'number') { - args.push(end.toString()); + args.push(end.toString()); } if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BLMOVE.ts b/packages/client/lib/commands/BLMOVE.ts index ee808e70fcc..c7e4844375f 100644 --- a/packages/client/lib/commands/BLMOVE.ts +++ b/packages/client/lib/commands/BLMOVE.ts @@ -1,23 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { ListSide } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, - sourceDirection: ListSide, - destinationDirection: ListSide, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + source: RedisArgument, + destination: RedisArgument, + sourceSide: ListSide, + destinationSide: ListSide, timeout: number -): RedisCommandArguments { + ) { return [ - 'BLMOVE', - source, - destination, - sourceDirection, - destinationDirection, - timeout.toString() + 'BLMOVE', + source, + destination, + sourceSide, + destinationSide, + timeout.toString() ]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BLMPOP.ts b/packages/client/lib/commands/BLMPOP.ts index 11bfad8b99b..ec9ab56829b 100644 --- a/packages/client/lib/commands/BLMPOP.ts +++ b/packages/client/lib/commands/BLMPOP.ts @@ -1,20 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformLMPopArguments, LMPopOptions, ListSide } from './generic-transformers'; +import { Command } from '../RESP/types'; +import { transformLMPopArguments, LMPopOptions, ListSide, RedisVariadicArgument } from './generic-transformers'; +import LMPOP from './LMPOP'; -export const FIRST_KEY_INDEX = 3; - -export function transformArguments( +export default { + FIRST_KEY_INDEX: 3, + IS_READ_ONLY: false, + transformArguments( timeout: number, - keys: RedisCommandArgument | Array, + keys: RedisVariadicArgument, side: ListSide, options?: LMPopOptions -): RedisCommandArguments { + ) { return transformLMPopArguments( - ['BLMPOP', timeout.toString()], - keys, - side, - options + ['BLMPOP', timeout.toString()], + keys, + side, + options ); -} - -export { transformReply } from './LMPOP'; + }, + transformReply: LMPOP.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BLPOP.ts b/packages/client/lib/commands/BLPOP.ts index 46ef41ad6f0..e96617e21d4 100644 --- a/packages/client/lib/commands/BLPOP.ts +++ b/packages/client/lib/commands/BLPOP.ts @@ -1,31 +1,23 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - keys: RedisCommandArgument | Array, +import { BlobStringReply, NullReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisVariadicArgument, timeout: number -): RedisCommandArguments { - const args = pushVerdictArguments(['BLPOP'], keys); - + ) { + const args = pushVariadicArguments(['BRPOP'], key); args.push(timeout.toString()); - return args; -} - -type BLPopRawReply = null | [RedisCommandArgument, RedisCommandArgument]; - -type BLPopReply = null | { - key: RedisCommandArgument; - element: RedisCommandArgument; -}; - -export function transformReply(reply: BLPopRawReply): BLPopReply { + }, + transformReply(reply: NullReply | [BlobStringReply, BlobStringReply]) { if (reply === null) return null; return { - key: reply[0], - element: reply[1] + key: reply[0], + element: reply[1] }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/BRPOP.ts b/packages/client/lib/commands/BRPOP.ts index b30e7e2cc29..f9c8aaa5037 100644 --- a/packages/client/lib/commands/BRPOP.ts +++ b/packages/client/lib/commands/BRPOP.ts @@ -1,17 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; +import BLPOP from './BLPOP'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument | Array, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisVariadicArgument, timeout: number -): RedisCommandArguments { - const args = pushVerdictArguments(['BRPOP'], key); - + ) { + const args = pushVariadicArguments(['BRPOP'], key); args.push(timeout.toString()); - return args; -} - -export { transformReply } from './BLPOP'; + }, + transformReply: BLPOP.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BRPOPLPUSH.ts b/packages/client/lib/commands/BRPOPLPUSH.ts index 72c3e4aa5b2..d342ea75725 100644 --- a/packages/client/lib/commands/BRPOPLPUSH.ts +++ b/packages/client/lib/commands/BRPOPLPUSH.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + source: RedisArgument, + destination: RedisArgument, timeout: number -): RedisCommandArguments { + ) { return ['BRPOPLPUSH', source, destination, timeout.toString()]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/BZMPOP.ts b/packages/client/lib/commands/BZMPOP.ts index e4e9699cbd4..b043d5fcf64 100644 --- a/packages/client/lib/commands/BZMPOP.ts +++ b/packages/client/lib/commands/BZMPOP.ts @@ -1,20 +1,34 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { SortedSetSide, transformZMPopArguments, ZMPopOptions } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { SortedSetSide, transformZMPopArguments, ZMPopOptions } from './generic-transformers'; -export const FIRST_KEY_INDEX = 3; +// export const FIRST_KEY_INDEX = 3; -export function transformArguments( - timeout: number, - keys: RedisCommandArgument | Array, - side: SortedSetSide, - options?: ZMPopOptions -): RedisCommandArguments { - return transformZMPopArguments( - ['BZMPOP', timeout.toString()], - keys, - side, - options - ); -} +// export function transformArguments( +// timeout: number, +// keys: RedisCommandArgument | Array, +// side: SortedSetSide, +// options?: ZMPopOptions +// ): RedisCommandArguments { +// return transformZMPopArguments( +// ['BZMPOP', timeout.toString()], +// keys, +// side, +// options +// ); +// } + +// export { transformReply } from './ZMPOP'; + + +// import { Command } from '../RESP/types'; +// import ZMPOP from './ZMPOP'; + +// export default { +// FIRST_KEY_INDEX: 3, +// IS_READ_ONLY: false, +// transformArguments() { +// return ['BZMPOP']; +// }, +// transformReply: ZMPOP.transformReply +// } as const satisfies Command; -export { transformReply } from './ZMPOP'; diff --git a/packages/client/lib/commands/BZPOPMAX.ts b/packages/client/lib/commands/BZPOPMAX.ts index 94a30fb8dce..250bc982b45 100644 --- a/packages/client/lib/commands/BZPOPMAX.ts +++ b/packages/client/lib/commands/BZPOPMAX.ts @@ -1,29 +1,29 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments, transformNumberInfinityReply, ZMember } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { pushVariadicArguments, transformDoubleReply, ZMember } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments( - key: RedisCommandArgument | Array, - timeout: number -): RedisCommandArguments { - const args = pushVerdictArguments(['BZPOPMAX'], key); +// export function transformArguments( +// key: RedisCommandArgument | Array, +// timeout: number +// ): RedisCommandArguments { +// const args = pushVariadicArguments(['BZPOPMAX'], key); - args.push(timeout.toString()); +// args.push(timeout.toString()); - return args; -} +// return args; +// } -type ZMemberRawReply = [key: RedisCommandArgument, value: RedisCommandArgument, score: RedisCommandArgument] | null; +// type ZMemberRawReply = [key: RedisCommandArgument, value: RedisCommandArgument, score: RedisCommandArgument] | null; -type BZPopMaxReply = (ZMember & { key: RedisCommandArgument }) | null; +// type BZPopMaxReply = (ZMember & { key: RedisCommandArgument }) | null; -export function transformReply(reply: ZMemberRawReply): BZPopMaxReply | null { - if (!reply) return null; +// export function transformReply(reply: ZMemberRawReply): BZPopMaxReply | null { +// if (!reply) return null; - return { - key: reply[0], - value: reply[1], - score: transformNumberInfinityReply(reply[2]) - }; -} +// return { +// key: reply[0], +// value: reply[1], +// score: transformDoubleReply(reply[2]) +// }; +// } diff --git a/packages/client/lib/commands/BZPOPMIN.ts b/packages/client/lib/commands/BZPOPMIN.ts index 40cb3d5dc75..967633697a4 100644 --- a/packages/client/lib/commands/BZPOPMIN.ts +++ b/packages/client/lib/commands/BZPOPMIN.ts @@ -1,17 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments( - key: RedisCommandArgument | Array, - timeout: number -): RedisCommandArguments { - const args = pushVerdictArguments(['BZPOPMIN'], key); +// export function transformArguments( +// key: RedisCommandArgument | Array, +// timeout: number +// ): RedisCommandArguments { +// const args = pushVariadicArguments(['BZPOPMIN'], key); - args.push(timeout.toString()); +// args.push(timeout.toString()); - return args; -} +// return args; +// } -export { transformReply } from './BZPOPMAX'; +// export { transformReply } from './BZPOPMAX'; diff --git a/packages/client/lib/commands/CLIENT_CACHING.spec.ts b/packages/client/lib/commands/CLIENT_CACHING.spec.ts index d9cb9a3f796..bf8e4ed12bf 100644 --- a/packages/client/lib/commands/CLIENT_CACHING.spec.ts +++ b/packages/client/lib/commands/CLIENT_CACHING.spec.ts @@ -1,20 +1,20 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './CLIENT_CACHING'; +import CLIENT_CACHING from './CLIENT_CACHING'; describe('CLIENT CACHING', () => { - describe('transformArguments', () => { - it('true', () => { - assert.deepEqual( - transformArguments(true), - ['CLIENT', 'CACHING', 'YES'] - ); - }); + describe('transformArguments', () => { + it('true', () => { + assert.deepEqual( + CLIENT_CACHING.transformArguments(true), + ['CLIENT', 'CACHING', 'YES'] + ); + }); - it('false', () => { - assert.deepEqual( - transformArguments(false), - ['CLIENT', 'CACHING', 'NO'] - ); - }); + it('false', () => { + assert.deepEqual( + CLIENT_CACHING.transformArguments(false), + ['CLIENT', 'CACHING', 'NO'] + ); }); + }); }); diff --git a/packages/client/lib/commands/CLIENT_CACHING.ts b/packages/client/lib/commands/CLIENT_CACHING.ts index bc2fbe41e9d..35edf889fa7 100644 --- a/packages/client/lib/commands/CLIENT_CACHING.ts +++ b/packages/client/lib/commands/CLIENT_CACHING.ts @@ -1,11 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(value: boolean): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(value: boolean) { return [ - 'CLIENT', - 'CACHING', - value ? 'YES' : 'NO' + 'CLIENT', + 'CACHING', + value ? 'YES' : 'NO' ]; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_GETNAME.spec.ts b/packages/client/lib/commands/CLIENT_GETNAME.spec.ts index 0a09713882f..cbd65768917 100644 --- a/packages/client/lib/commands/CLIENT_GETNAME.spec.ts +++ b/packages/client/lib/commands/CLIENT_GETNAME.spec.ts @@ -1,11 +1,11 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './CLIENT_GETNAME'; +import CLIENT_GETNAME from './CLIENT_GETNAME'; describe('CLIENT GETNAME', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'GETNAME'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_GETNAME.transformArguments(), + ['CLIENT', 'GETNAME'] + ); + }); }); diff --git a/packages/client/lib/commands/CLIENT_GETNAME.ts b/packages/client/lib/commands/CLIENT_GETNAME.ts index da00539d7fb..5e3272553e9 100644 --- a/packages/client/lib/commands/CLIENT_GETNAME.ts +++ b/packages/client/lib/commands/CLIENT_GETNAME.ts @@ -1,7 +1,12 @@ -import { RedisCommandArguments } from '.'; +import { BlobStringReply, NullReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['CLIENT', 'GETNAME']; -} - -export declare function transformReply(): string | null; +export default { + IS_READ_ONLY: true, + transformArguments() { + return [ + 'CLIENT', + 'GETNAME' + ]; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts b/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts index 09dd9677e32..057eb237129 100644 --- a/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts +++ b/packages/client/lib/commands/CLIENT_GETREDIR.spec.ts @@ -1,11 +1,11 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './CLIENT_GETREDIR'; +import CLIENT_GETREDIR from './CLIENT_GETREDIR'; describe('CLIENT GETREDIR', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'GETREDIR'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_GETREDIR.transformArguments(), + ['CLIENT', 'GETREDIR'] + ); + }); }); diff --git a/packages/client/lib/commands/CLIENT_GETREDIR.ts b/packages/client/lib/commands/CLIENT_GETREDIR.ts index d192adf284a..c2e68de5b6f 100644 --- a/packages/client/lib/commands/CLIENT_GETREDIR.ts +++ b/packages/client/lib/commands/CLIENT_GETREDIR.ts @@ -1,7 +1,9 @@ -import { RedisCommandArguments } from '.'; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['CLIENT', 'GETREDIR']; -} - -export declare function transformReply(): number; +export default { + IS_READ_ONLY: true, + transformArguments() { + return ['CLIENT', 'GETREDIR'] + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_ID.spec.ts b/packages/client/lib/commands/CLIENT_ID.spec.ts index 6792a8c31be..28865a4ade7 100644 --- a/packages/client/lib/commands/CLIENT_ID.spec.ts +++ b/packages/client/lib/commands/CLIENT_ID.spec.ts @@ -1,19 +1,19 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './CLIENT_ID'; +import CLIENT_ID from './CLIENT_ID'; describe('CLIENT ID', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'ID'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_ID.transformArguments(), + ['CLIENT', 'ID'] + ); + }); - testUtils.testWithClient('client.clientId', async client => { - assert.equal( - typeof (await client.clientId()), - 'number' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.clientId', async client => { + assert.equal( + typeof (await client.clientId()), + 'number' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_ID.ts b/packages/client/lib/commands/CLIENT_ID.ts index a57e392ade6..cc298ed122e 100644 --- a/packages/client/lib/commands/CLIENT_ID.ts +++ b/packages/client/lib/commands/CLIENT_ID.ts @@ -1,7 +1,9 @@ -export const IS_READ_ONLY = true; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + IS_READ_ONLY: true, + transformArguments() { return ['CLIENT', 'ID']; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts index 7f6b6e1884e..1bd9fc2f25f 100644 --- a/packages/client/lib/commands/CLIENT_INFO.ts +++ b/packages/client/lib/commands/CLIENT_INFO.ts @@ -1,89 +1,115 @@ -export const IS_READ_ONLY = true; - -export function transformArguments(): Array { - return ['CLIENT', 'INFO']; -} +import { Command, VerbatimStringReply } from '../RESP/types'; export interface ClientInfoReply { - id: number; - addr: string; - laddr?: string; // 6.2 - fd: number; - name: string; - age: number; - idle: number; - flags: string; - db: number; - sub: number; - psub: number; - ssub?: number; // 7.0.3 - multi: number; - qbuf: number; - qbufFree: number; - argvMem?: number; // 6.0 - multiMem?: number; // 7.0 - obl: number; - oll: number; - omem: number; - totMem?: number; // 6.0 - events: string; - cmd: string; - user?: string; // 6.0 - redir?: number; // 6.2 - resp?: number; // 7.0 + id: number; + addr: string; + /** + * available since 6.2 + */ + laddr?: string; + fd: number; + name: string; + age: number; + idle: number; + flags: string; + db: number; + sub: number; + psub: number; + /** + * available since 7.0.3 + */ + ssub?: number; + multi: number; + qbuf: number; + qbufFree: number; + /** + * available since 6.0 + */ + argvMem?: number; + /** + * available since 7.0 + */ + multiMem?: number; + obl: number; + oll: number; + omem: number; + /** + * available since 6.0 + */ + totMem?: number; + events: string; + cmd: string; + /** + * available since 6.0 + */ + user?: string; + /** + * available since 6.2 + */ + redir?: number; + /** + * available since 7.0 + */ + resp?: number; } const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g; -export function transformReply(rawReply: string): ClientInfoReply { +export default { + IS_READ_ONLY: true, + transformArguments() { + return ['CLIENT', 'INFO'] + }, + transformReply(rawReply: VerbatimStringReply) { const map: Record = {}; - for (const item of rawReply.matchAll(CLIENT_INFO_REGEX)) { - map[item[1]] = item[2]; + for (const item of rawReply.toString().matchAll(CLIENT_INFO_REGEX)) { + map[item[1]] = item[2]; } const reply: ClientInfoReply = { - id: Number(map.id), - addr: map.addr, - fd: Number(map.fd), - name: map.name, - age: Number(map.age), - idle: Number(map.idle), - flags: map.flags, - db: Number(map.db), - sub: Number(map.sub), - psub: Number(map.psub), - multi: Number(map.multi), - qbuf: Number(map.qbuf), - qbufFree: Number(map['qbuf-free']), - argvMem: Number(map['argv-mem']), - obl: Number(map.obl), - oll: Number(map.oll), - omem: Number(map.omem), - totMem: Number(map['tot-mem']), - events: map.events, - cmd: map.cmd, - user: map.user + id: Number(map.id), + addr: map.addr, + fd: Number(map.fd), + name: map.name, + age: Number(map.age), + idle: Number(map.idle), + flags: map.flags, + db: Number(map.db), + sub: Number(map.sub), + psub: Number(map.psub), + multi: Number(map.multi), + qbuf: Number(map.qbuf), + qbufFree: Number(map['qbuf-free']), + argvMem: Number(map['argv-mem']), + obl: Number(map.obl), + oll: Number(map.oll), + omem: Number(map.omem), + totMem: Number(map['tot-mem']), + events: map.events, + cmd: map.cmd, + user: map.user }; if (map.laddr !== undefined) { - reply.laddr = map.laddr; + reply.laddr = map.laddr; } if (map.redir !== undefined) { - reply.redir = Number(map.redir); + reply.redir = Number(map.redir); } if (map.ssub !== undefined) { - reply.ssub = Number(map.ssub); + reply.ssub = Number(map.ssub); } if (map['multi-mem'] !== undefined) { - reply.multiMem = Number(map['multi-mem']); + reply.multiMem = Number(map['multi-mem']); } if (map.resp !== undefined) { - reply.resp = Number(map.resp); + reply.resp = Number(map.resp); } return reply; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_KILL.spec.ts b/packages/client/lib/commands/CLIENT_KILL.spec.ts index 2fe894f3610..4a64cb2c6e3 100644 --- a/packages/client/lib/commands/CLIENT_KILL.spec.ts +++ b/packages/client/lib/commands/CLIENT_KILL.spec.ts @@ -2,109 +2,109 @@ import { strict as assert } from 'assert'; import { ClientKillFilters, transformArguments } from './CLIENT_KILL'; describe('CLIENT KILL', () => { - describe('transformArguments', () => { - it('ADDRESS', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.ADDRESS, - address: 'ip:6379' - }), - ['CLIENT', 'KILL', 'ADDR', 'ip:6379'] - ); - }); + describe('transformArguments', () => { + it('ADDRESS', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.ADDRESS, + address: 'ip:6379' + }), + ['CLIENT', 'KILL', 'ADDR', 'ip:6379'] + ); + }); - it('LOCAL_ADDRESS', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.LOCAL_ADDRESS, - localAddress: 'ip:6379' - }), - ['CLIENT', 'KILL', 'LADDR', 'ip:6379'] - ); - }); + it('LOCAL_ADDRESS', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.LOCAL_ADDRESS, + localAddress: 'ip:6379' + }), + ['CLIENT', 'KILL', 'LADDR', 'ip:6379'] + ); + }); - describe('ID', () => { - it('string', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.ID, - id: '1' - }), - ['CLIENT', 'KILL', 'ID', '1'] - ); - }); + describe('ID', () => { + it('string', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.ID, + id: '1' + }), + ['CLIENT', 'KILL', 'ID', '1'] + ); + }); - it('number', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.ID, - id: 1 - }), - ['CLIENT', 'KILL', 'ID', '1'] - ); - }); - }); + it('number', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.ID, + id: 1 + }), + ['CLIENT', 'KILL', 'ID', '1'] + ); + }); + }); - it('TYPE', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.TYPE, - type: 'master' - }), - ['CLIENT', 'KILL', 'TYPE', 'master'] - ); - }); + it('TYPE', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.TYPE, + type: 'master' + }), + ['CLIENT', 'KILL', 'TYPE', 'master'] + ); + }); - it('USER', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.USER, - username: 'username' - }), - ['CLIENT', 'KILL', 'USER', 'username'] - ); - }); + it('USER', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.USER, + username: 'username' + }), + ['CLIENT', 'KILL', 'USER', 'username'] + ); + }); + + describe('SKIP_ME', () => { + it('undefined', () => { + assert.deepEqual( + transformArguments(ClientKillFilters.SKIP_ME), + ['CLIENT', 'KILL', 'SKIPME'] + ); + }); - describe('SKIP_ME', () => { - it('undefined', () => { - assert.deepEqual( - transformArguments(ClientKillFilters.SKIP_ME), - ['CLIENT', 'KILL', 'SKIPME'] - ); - }); + it('true', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.SKIP_ME, + skipMe: true + }), + ['CLIENT', 'KILL', 'SKIPME', 'yes'] + ); + }); - it('true', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.SKIP_ME, - skipMe: true - }), - ['CLIENT', 'KILL', 'SKIPME', 'yes'] - ); - }); + it('false', () => { + assert.deepEqual( + transformArguments({ + filter: ClientKillFilters.SKIP_ME, + skipMe: false + }), + ['CLIENT', 'KILL', 'SKIPME', 'no'] + ); + }); + }); - it('false', () => { - assert.deepEqual( - transformArguments({ - filter: ClientKillFilters.SKIP_ME, - skipMe: false - }), - ['CLIENT', 'KILL', 'SKIPME', 'no'] - ); - }); - }); - - it('TYPE & SKIP_ME', () => { - assert.deepEqual( - transformArguments([ - { - filter: ClientKillFilters.TYPE, - type: 'master' - }, - ClientKillFilters.SKIP_ME - ]), - ['CLIENT', 'KILL', 'TYPE', 'master', 'SKIPME'] - ); - }); + it('TYPE & SKIP_ME', () => { + assert.deepEqual( + transformArguments([ + { + filter: ClientKillFilters.TYPE, + type: 'master' + }, + ClientKillFilters.SKIP_ME + ]), + ['CLIENT', 'KILL', 'TYPE', 'master', 'SKIPME'] + ); }); + }); }); diff --git a/packages/client/lib/commands/CLIENT_KILL.ts b/packages/client/lib/commands/CLIENT_KILL.ts index adb2a5a6569..c1663f80e3a 100644 --- a/packages/client/lib/commands/CLIENT_KILL.ts +++ b/packages/client/lib/commands/CLIENT_KILL.ts @@ -1,95 +1,99 @@ -import { RedisCommandArguments } from '.'; - -export enum ClientKillFilters { - ADDRESS = 'ADDR', - LOCAL_ADDRESS = 'LADDR', - ID = 'ID', - TYPE = 'TYPE', - USER = 'USER', - SKIP_ME = 'SKIPME' -} +import { RedisArgument, NumberReply, Command } from '../RESP/types'; + +export const CLIENT_KILL_FILTERS = { + ADDRESS: 'ADDR', + LOCAL_ADDRESS: 'LADDR', + ID: 'ID', + TYPE: 'TYPE', + USER: 'USER', + SKIP_ME: 'SKIPME' +} as const; -interface KillFilter { - filter: T; +type CLIENT_KILL_FILTERS = typeof CLIENT_KILL_FILTERS; + +export interface ClientKillFilterCommon { + filter: T; } -interface KillAddress extends KillFilter { - address: `${string}:${number}`; +export interface ClientKillAddress extends ClientKillFilterCommon { + address: `${string}:${number}`; } -interface KillLocalAddress extends KillFilter { - localAddress: `${string}:${number}`; +export interface ClientKillLocalAddress extends ClientKillFilterCommon { + localAddress: `${string}:${number}`; } -interface KillId extends KillFilter { - id: number | `${number}`; +export interface ClientKillId extends ClientKillFilterCommon { + id: number | `${number}`; } -interface KillType extends KillFilter { - type: 'normal' | 'master' | 'replica' | 'pubsub'; +export interface ClientKillType extends ClientKillFilterCommon { + type: 'normal' | 'master' | 'replica' | 'pubsub'; } -interface KillUser extends KillFilter { - username: string; +export interface ClientKillUser extends ClientKillFilterCommon { + username: string; } -type KillSkipMe = ClientKillFilters.SKIP_ME | (KillFilter & { - skipMe: boolean; +export type ClientKillSkipMe = CLIENT_KILL_FILTERS['SKIP_ME'] | (ClientKillFilterCommon & { + skipMe: boolean; }); -type KillFilters = KillAddress | KillLocalAddress | KillId | KillType | KillUser | KillSkipMe; +export type ClientKillFilter = ClientKillAddress | ClientKillLocalAddress | ClientKillId | ClientKillType | ClientKillUser | ClientKillSkipMe; -export function transformArguments(filters: KillFilters | Array): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(filters: ClientKillFilter | Array) { const args = ['CLIENT', 'KILL']; if (Array.isArray(filters)) { - for (const filter of filters) { - pushFilter(args, filter); - } + for (const filter of filters) { + pushFilter(args, filter); + } } else { - pushFilter(args, filters); + pushFilter(args, filters); } return args; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; + +function pushFilter(args: Array, filter: ClientKillFilter): void { + if (filter === CLIENT_KILL_FILTERS.SKIP_ME) { + args.push('SKIPME'); + return; + } + + args.push(filter.filter); + + switch (filter.filter) { + case CLIENT_KILL_FILTERS.ADDRESS: + args.push(filter.address); + break; + + case CLIENT_KILL_FILTERS.LOCAL_ADDRESS: + args.push(filter.localAddress); + break; + + case CLIENT_KILL_FILTERS.ID: + args.push( + typeof filter.id === 'number' ? + filter.id.toString() : + filter.id + ); + break; + + case CLIENT_KILL_FILTERS.TYPE: + args.push(filter.type); + break; + + case CLIENT_KILL_FILTERS.USER: + args.push(filter.username); + break; + + case CLIENT_KILL_FILTERS.SKIP_ME: + args.push(filter.skipMe ? 'yes' : 'no'); + break; + } } - -function pushFilter(args: RedisCommandArguments, filter: KillFilters): void { - if (filter === ClientKillFilters.SKIP_ME) { - args.push('SKIPME'); - return; - } - - args.push(filter.filter); - - switch(filter.filter) { - case ClientKillFilters.ADDRESS: - args.push(filter.address); - break; - - case ClientKillFilters.LOCAL_ADDRESS: - args.push(filter.localAddress); - break; - - case ClientKillFilters.ID: - args.push( - typeof filter.id === 'number' ? - filter.id.toString() : - filter.id - ); - break; - - case ClientKillFilters.TYPE: - args.push(filter.type); - break; - - case ClientKillFilters.USER: - args.push(filter.username); - break; - - case ClientKillFilters.SKIP_ME: - args.push(filter.skipMe ? 'yes' : 'no'); - break; - } -} - -export declare function transformReply(): number; diff --git a/packages/client/lib/commands/CLIENT_LIST.ts b/packages/client/lib/commands/CLIENT_LIST.ts index 6f71dc7d999..b1c2e5d2db4 100644 --- a/packages/client/lib/commands/CLIENT_LIST.ts +++ b/packages/client/lib/commands/CLIENT_LIST.ts @@ -1,43 +1,43 @@ -import { RedisCommandArguments, RedisCommandArgument } from '.'; -import { pushVerdictArguments } from './generic-transformers'; -import { transformReply as transformClientInfoReply, ClientInfoReply } from './CLIENT_INFO'; +import { RedisArgument, VerbatimStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; +import CLIENT_INFO, { ClientInfoReply } from './CLIENT_INFO'; -interface ListFilterType { - TYPE: 'NORMAL' | 'MASTER' | 'REPLICA' | 'PUBSUB'; - ID?: never; +export interface ListFilterType { + TYPE: 'NORMAL' | 'MASTER' | 'REPLICA' | 'PUBSUB'; + ID?: never; } -interface ListFilterId { - ID: Array; - TYPE?: never; +export interface ListFilterId { + ID: Array; + TYPE?: never; } export type ListFilter = ListFilterType | ListFilterId; -export const IS_READ_ONLY = true; - -export function transformArguments(filter?: ListFilter): RedisCommandArguments { - let args: RedisCommandArguments = ['CLIENT', 'LIST']; +export default { + IS_READ_ONLY: true, + transformArguments(filter?: ListFilter) { + let args: Array = ['CLIENT', 'LIST']; if (filter) { - if (filter.TYPE !== undefined) { - args.push('TYPE', filter.TYPE); - } else { - args.push('ID'); - args = pushVerdictArguments(args, filter.ID); - } + if (filter.TYPE !== undefined) { + args.push('TYPE', filter.TYPE); + } else { + args.push('ID'); + args = pushVariadicArguments(args, filter.ID); + } } return args; -} - -export function transformReply(rawReply: string): Array { - const split = rawReply.split('\n'), - length = split.length - 1, - reply: Array = []; + }, + transformReply(rawReply: VerbatimStringReply): Array { + const split = rawReply.toString().split('\n'), + length = split.length - 1, + reply: Array = []; for (let i = 0; i < length; i++) { - reply.push(transformClientInfoReply(split[i])); + reply.push(CLIENT_INFO.transformReply(split[i] as VerbatimStringReply)); } - + return reply; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_NO-EVICT.ts b/packages/client/lib/commands/CLIENT_NO-EVICT.ts index 86edbde1d23..226405d0320 100644 --- a/packages/client/lib/commands/CLIENT_NO-EVICT.ts +++ b/packages/client/lib/commands/CLIENT_NO-EVICT.ts @@ -1,11 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(value: boolean): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(value: boolean) { return [ - 'CLIENT', - 'NO-EVICT', - value ? 'ON' : 'OFF' + 'CLIENT', + 'NO-EVICT', + value ? 'ON' : 'OFF' ]; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_PAUSE.ts b/packages/client/lib/commands/CLIENT_PAUSE.ts index 090002272c9..b01e356cf54 100644 --- a/packages/client/lib/commands/CLIENT_PAUSE.ts +++ b/packages/client/lib/commands/CLIENT_PAUSE.ts @@ -1,20 +1,19 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments( - timeout: number, - mode?: 'WRITE' | 'ALL' -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(timeout: number, mode?: 'WRITE' | 'ALL') { const args = [ - 'CLIENT', - 'PAUSE', - timeout.toString() + 'CLIENT', + 'PAUSE', + timeout.toString() ]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_SETNAME.ts b/packages/client/lib/commands/CLIENT_SETNAME.ts index f5cf1c786fb..9208bba9243 100644 --- a/packages/client/lib/commands/CLIENT_SETNAME.ts +++ b/packages/client/lib/commands/CLIENT_SETNAME.ts @@ -1,7 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(name: RedisCommandArgument): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(name: RedisArgument) { return ['CLIENT', 'SETNAME', name]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLIENT_TRACKING.ts b/packages/client/lib/commands/CLIENT_TRACKING.ts index c70702706e4..03926b5300f 100644 --- a/packages/client/lib/commands/CLIENT_TRACKING.ts +++ b/packages/client/lib/commands/CLIENT_TRACKING.ts @@ -1,83 +1,83 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -interface CommonOptions { - REDIRECT?: number; - NOLOOP?: boolean; -} +// interface CommonOptions { +// REDIRECT?: number; +// NOLOOP?: boolean; +// } -interface BroadcastOptions { - BCAST?: boolean; - PREFIX?: RedisCommandArgument | Array; -} +// interface BroadcastOptions { +// BCAST?: boolean; +// PREFIX?: RedisCommandArgument | Array; +// } -interface OptInOptions { - OPTIN?: boolean; -} +// interface OptInOptions { +// OPTIN?: boolean; +// } -interface OptOutOptions { - OPTOUT?: boolean; -} +// interface OptOutOptions { +// OPTOUT?: boolean; +// } -type ClientTrackingOptions = CommonOptions & ( - BroadcastOptions | - OptInOptions | - OptOutOptions -); +// type ClientTrackingOptions = CommonOptions & ( +// BroadcastOptions | +// OptInOptions | +// OptOutOptions +// ); -export function transformArguments( - mode: M, - options?: M extends true ? ClientTrackingOptions : undefined -): RedisCommandArguments { - const args: RedisCommandArguments = [ - 'CLIENT', - 'TRACKING', - mode ? 'ON' : 'OFF' - ]; +// export function transformArguments( +// mode: M, +// options?: M extends true ? ClientTrackingOptions : undefined +// ): RedisCommandArguments { +// const args: RedisCommandArguments = [ +// 'CLIENT', +// 'TRACKING', +// mode ? 'ON' : 'OFF' +// ]; - if (mode) { - if (options?.REDIRECT) { - args.push( - 'REDIRECT', - options.REDIRECT.toString() - ); - } +// if (mode) { +// if (options?.REDIRECT) { +// args.push( +// 'REDIRECT', +// options.REDIRECT.toString() +// ); +// } - if (isBroadcast(options)) { - args.push('BCAST'); +// if (isBroadcast(options)) { +// args.push('BCAST'); - if (options?.PREFIX) { - if (Array.isArray(options.PREFIX)) { - for (const prefix of options.PREFIX) { - args.push('PREFIX', prefix); - } - } else { - args.push('PREFIX', options.PREFIX); - } - } - } else if (isOptIn(options)) { - args.push('OPTIN'); - } else if (isOptOut(options)) { - args.push('OPTOUT'); - } +// if (options?.PREFIX) { +// if (Array.isArray(options.PREFIX)) { +// for (const prefix of options.PREFIX) { +// args.push('PREFIX', prefix); +// } +// } else { +// args.push('PREFIX', options.PREFIX); +// } +// } +// } else if (isOptIn(options)) { +// args.push('OPTIN'); +// } else if (isOptOut(options)) { +// args.push('OPTOUT'); +// } - if (options?.NOLOOP) { - args.push('NOLOOP'); - } - } +// if (options?.NOLOOP) { +// args.push('NOLOOP'); +// } +// } - return args; -} +// return args; +// } -function isBroadcast(options?: ClientTrackingOptions): options is BroadcastOptions { - return (options as BroadcastOptions)?.BCAST === true; -} +// function isBroadcast(options?: ClientTrackingOptions): options is BroadcastOptions { +// return (options as BroadcastOptions)?.BCAST === true; +// } -function isOptIn(options?: ClientTrackingOptions): options is OptInOptions { - return (options as OptInOptions)?.OPTIN === true; -} +// function isOptIn(options?: ClientTrackingOptions): options is OptInOptions { +// return (options as OptInOptions)?.OPTIN === true; +// } -function isOptOut(options?: ClientTrackingOptions): options is OptOutOptions { - return (options as OptOutOptions)?.OPTOUT === true; -} +// function isOptOut(options?: ClientTrackingOptions): options is OptOutOptions { +// return (options as OptOutOptions)?.OPTOUT === true; +// } -export declare function transformReply(): 'OK' | Buffer; +// export declare function transformReply(): 'OK' | Buffer; diff --git a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts index 7c883fc6997..fd897bb67c6 100644 --- a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts +++ b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts @@ -1,28 +1,28 @@ -import { RedisCommandArguments } from '.'; +// import { RedisCommandArguments } from '.'; -export function transformArguments(): RedisCommandArguments { - return ['CLIENT', 'TRACKINGINFO']; -} +// export function transformArguments(): RedisCommandArguments { +// return ['CLIENT', 'TRACKINGINFO']; +// } -type RawReply = [ - 'flags', - Array, - 'redirect', - number, - 'prefixes', - Array -]; +// type RawReply = [ +// 'flags', +// Array, +// 'redirect', +// number, +// 'prefixes', +// Array +// ]; -interface Reply { - flags: Set; - redirect: number; - prefixes: Array; -} +// interface Reply { +// flags: Set; +// redirect: number; +// prefixes: Array; +// } -export function transformReply(reply: RawReply): Reply { - return { - flags: new Set(reply[1]), - redirect: reply[3], - prefixes: reply[5] - }; -} +// export function transformReply(reply: RawReply): Reply { +// return { +// flags: new Set(reply[1]), +// redirect: reply[3], +// prefixes: reply[5] +// }; +// } diff --git a/packages/client/lib/commands/CLIENT_UNPAUSE.ts b/packages/client/lib/commands/CLIENT_UNPAUSE.ts index e139436d004..b2baf9c4bea 100644 --- a/packages/client/lib/commands/CLIENT_UNPAUSE.ts +++ b/packages/client/lib/commands/CLIENT_UNPAUSE.ts @@ -1,7 +1,9 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments() { return ['CLIENT', 'UNPAUSE']; -} - -export declare function transformReply(): 'OK' | Buffer; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts index 6cd357fb823..0841b42eca6 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTS.ts @@ -1,11 +1,13 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictNumberArguments } from './generic-transformers'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { pushVariadicNumberArguments } from './generic-transformers'; -export function transformArguments(slots: number | Array): RedisCommandArguments { - return pushVerdictNumberArguments( - ['CLUSTER', 'ADDSLOTS'], - slots +export default { + IS_READ_ONLY: true, + transformArguments(slots: number | Array) { + return pushVariadicNumberArguments( + ['CLUSTER', 'ADDSLOTS'], + slots ); -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts index 6a8d6dc668f..ebbd4529e1e 100644 --- a/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts +++ b/packages/client/lib/commands/CLUSTER_ADDSLOTSRANGE.ts @@ -1,13 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; import { pushSlotRangesArguments, SlotRange } from './generic-transformers'; -export function transformArguments( - ranges: SlotRange | Array -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(ranges: SlotRange | Array) { return pushSlotRangesArguments( - ['CLUSTER', 'ADDSLOTSRANGE'], - ranges + ['CLUSTER', 'ADDSLOTSRANGE'], + ranges ); -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts index 7f81c8fdc42..c2587e82ae9 100644 --- a/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts +++ b/packages/client/lib/commands/CLUSTER_BUMPEPOCH.ts @@ -1,5 +1,9 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'BUMPEPOCH']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'BUMPED' | 'STILL'; +export default { + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'BUMPEPOCH']; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'BUMPED' | 'STILL'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts index 3fbc33052f8..1c33ca7453c 100644 --- a/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts +++ b/packages/client/lib/commands/CLUSTER_COUNT-FAILURE-REPORTS.ts @@ -1,5 +1,9 @@ -export function transformArguments(nodeId: string): Array { - return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId]; -} +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export declare function transformReply(): number; +export default { + IS_READ_ONLY: true, + transformArguments(nodeId: RedisArgument) { + return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts index a5ff75e58a9..939d09d2082 100644 --- a/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_COUNTKEYSINSLOT.ts @@ -1,5 +1,9 @@ -export function transformArguments(slot: number): Array { - return ['CLUSTER', 'COUNTKEYSINSLOT', slot.toString()]; -} +import { NumberReply, Command } from '../RESP/types'; -export declare function transformReply(): number; +export default { + IS_READ_ONLY: true, + transformArguments(slot: number) { + return ['CLUSTER', 'COUNT-FAILURE-REPORTS', slot.toString()]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts index bf8d9c18900..f9f408fa936 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTS.ts @@ -1,11 +1,13 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictNumberArguments } from './generic-transformers'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { pushVariadicNumberArguments } from './generic-transformers'; -export function transformArguments(slots: number | Array): RedisCommandArguments { - return pushVerdictNumberArguments( - ['CLUSTER', 'DELSLOTS'], - slots +export default { + IS_READ_ONLY: true, + transformArguments(slots: number | Array) { + return pushVariadicNumberArguments( + ['CLUSTER', 'DELSLOTS'], + slots ); -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts index b136113c65f..81e46ba2dc7 100644 --- a/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts +++ b/packages/client/lib/commands/CLUSTER_DELSLOTSRANGE.ts @@ -1,13 +1,13 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; import { pushSlotRangesArguments, SlotRange } from './generic-transformers'; -export function transformArguments( - ranges: SlotRange | Array -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(ranges: SlotRange | Array) { return pushSlotRangesArguments( - ['CLUSTER', 'DELSLOTSRANGE'], - ranges + ['CLUSTER', 'DELSLOTSRANGE'], + ranges ); -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FAILOVER.ts b/packages/client/lib/commands/CLUSTER_FAILOVER.ts index 9bc4b69f343..948d1f4864e 100644 --- a/packages/client/lib/commands/CLUSTER_FAILOVER.ts +++ b/packages/client/lib/commands/CLUSTER_FAILOVER.ts @@ -1,16 +1,22 @@ -export enum FailoverModes { - FORCE = 'FORCE', - TAKEOVER = 'TAKEOVER' -} +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(mode?: FailoverModes): Array { +export const FAILOVER_MODES = { + FORCE: 'FORCE', + TAKEOVER: 'TAKEOVER' +} as const; + +export type FailoverModes = typeof FAILOVER_MODES[keyof typeof FAILOVER_MODES]; + +export default { + IS_READ_ONLY: true, + transformArguments(mode?: FailoverModes) { const args = ['CLUSTER', 'FAILOVER']; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts index dfb1e1ccde8..d4f783cd114 100644 --- a/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_FLUSHSLOTS.ts @@ -1,5 +1,9 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'FLUSHSLOTS']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'FLUSHSLOTS']; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_FORGET.ts b/packages/client/lib/commands/CLUSTER_FORGET.ts index fc557073aeb..19bc4657e4b 100644 --- a/packages/client/lib/commands/CLUSTER_FORGET.ts +++ b/packages/client/lib/commands/CLUSTER_FORGET.ts @@ -1,5 +1,9 @@ -export function transformArguments(nodeId: string): Array { - return ['CLUSTER', 'FORGET', nodeId]; -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + IS_READ_ONLY: true, + transformArguments(nodeId: RedisArgument) { + return ['CLUSTER', 'FORGET', nodeId]; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts index ec75b7b7336..b9970a07db8 100644 --- a/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_GETKEYSINSLOT.ts @@ -1,5 +1,9 @@ -export function transformArguments(slot: number, count: number): Array { - return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; -} +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export declare function transformReply(): Array; +export default { + IS_READ_ONLY: true, + transformArguments(slot: number, count: number) { + return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts index 0af524ff128..fc68287eae5 100644 --- a/packages/client/lib/commands/CLUSTER_KEYSLOT.ts +++ b/packages/client/lib/commands/CLUSTER_KEYSLOT.ts @@ -1,5 +1,8 @@ -export function transformArguments(key: string): Array { - return ['CLUSTER', 'KEYSLOT', key]; -} +import { Command, NumberReply, RedisArgument } from '../RESP/types'; -export declare function transformReply(): number; +export default { + transformArguments(key: RedisArgument) { + return ['CLUSTER', 'KEYSLOT', key]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_MEET.ts b/packages/client/lib/commands/CLUSTER_MEET.ts index e6ce1c1fce4..3e5d343b6f1 100644 --- a/packages/client/lib/commands/CLUSTER_MEET.ts +++ b/packages/client/lib/commands/CLUSTER_MEET.ts @@ -1,5 +1,8 @@ -export function transformArguments(ip: string, port: number): Array { - return ['CLUSTER', 'MEET', ip, port.toString()]; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + transformArguments(host: string, port: number) { + return ['CLUSTER', 'MEET', host, port.toString()]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_MYID.ts b/packages/client/lib/commands/CLUSTER_MYID.ts index 2b61684634d..a7a4184f9c4 100644 --- a/packages/client/lib/commands/CLUSTER_MYID.ts +++ b/packages/client/lib/commands/CLUSTER_MYID.ts @@ -1,5 +1,8 @@ -export function transformArguments(): Array { - return ['CLUSTER', 'MYID']; -} +import { BlobStringReply, Command } from "../RESP/types"; -export declare function transformReply(): string; +export default { + transformArguments() { + return ['CLUSTER', 'MYID']; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_REPLICATE.ts b/packages/client/lib/commands/CLUSTER_REPLICATE.ts index c74e1ec5960..c589ad33d71 100644 --- a/packages/client/lib/commands/CLUSTER_REPLICATE.ts +++ b/packages/client/lib/commands/CLUSTER_REPLICATE.ts @@ -1,5 +1,8 @@ -export function transformArguments(nodeId: string): Array { - return ['CLUSTER', 'REPLICATE', nodeId]; -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): 'OK'; +export default { + transformArguments(nodeId: RedisArgument) { + return ['CLUSTER', 'REPLICATE', nodeId]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_SLOTS.ts b/packages/client/lib/commands/CLUSTER_SLOTS.ts index 20d9782dd9e..5d4e5d7d0b8 100644 --- a/packages/client/lib/commands/CLUSTER_SLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_SLOTS.ts @@ -1,46 +1,39 @@ -import { RedisCommandArguments } from '.'; +import { NumberReply, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { - return ['CLUSTER', 'SLOTS']; -} - -type ClusterSlotsRawNode = [ip: string, port: number, id: string]; +type RawNode = [ + host: BlobStringReply, + port: NumberReply, + id: BlobStringReply +]; -type ClusterSlotsRawReply = Array<[ - from: number, - to: number, - master: ClusterSlotsRawNode, - ...replicas: Array +type ClusterSlotsRawReply = ArrayReply<[ + from: NumberReply, + to: NumberReply, + master: RawNode, + ...replicas: Array ]>; -export interface ClusterSlotsNode { - ip: string; - port: number; - id: string; -}; +export type ClusterSlotsNode = ReturnType; -export type ClusterSlotsReply = Array<{ - from: number; - to: number; - master: ClusterSlotsNode; - replicas: Array; -}>; - -export function transformReply(reply: ClusterSlotsRawReply): ClusterSlotsReply { - return reply.map(([from, to, master, ...replicas]) => { - return { - from, - to, - master: transformNode(master), - replicas: replicas.map(transformNode) - }; - }); -} +export default { + IS_READ_ONLY: true, + transformArguments() { + return ['CLUSTER', 'SLOTS']; + }, + transformReply(reply: ClusterSlotsRawReply) { + return reply.map(([from, to, master, ...replicas]) => ({ + from, + to, + master: transformNode(master), + replicas: replicas.map(transformNode) + })); + } +} as const satisfies Command; -function transformNode([ip, port, id]: ClusterSlotsRawNode): ClusterSlotsNode { - return { - ip, - port, - id - }; +function transformNode([host, port, id ]: RawNode) { + return { + host, + port, + id + }; } diff --git a/packages/client/lib/commands/COMMAND.ts b/packages/client/lib/commands/COMMAND.ts index b6ee50b2f4c..a67ae1012df 100644 --- a/packages/client/lib/commands/COMMAND.ts +++ b/packages/client/lib/commands/COMMAND.ts @@ -1,12 +1,12 @@ -import { RedisCommandArguments } from '.'; -import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; +// import { RedisCommandArguments } from '.'; +// import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments(): RedisCommandArguments { - return ['COMMAND']; -} +// export function transformArguments(): RedisCommandArguments { +// return ['COMMAND']; +// } -export function transformReply(reply: Array): Array { - return reply.map(transformCommandReply); -} +// export function transformReply(reply: Array): Array { +// return reply.map(transformCommandReply); +// } diff --git a/packages/client/lib/commands/COMMAND_COUNT.ts b/packages/client/lib/commands/COMMAND_COUNT.ts index 34c6a088da6..f1cd06702e8 100644 --- a/packages/client/lib/commands/COMMAND_COUNT.ts +++ b/packages/client/lib/commands/COMMAND_COUNT.ts @@ -1,9 +1,9 @@ -import { RedisCommandArguments } from '.'; +// import { RedisCommandArguments } from '.'; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments(): RedisCommandArguments { - return ['COMMAND', 'COUNT']; -} +// export function transformArguments(): RedisCommandArguments { +// return ['COMMAND', 'COUNT']; +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/COMMAND_GETKEYS.ts b/packages/client/lib/commands/COMMAND_GETKEYS.ts index 6762fe4b58a..fb43798c1b3 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYS.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments(args: Array): RedisCommandArguments { - return ['COMMAND', 'GETKEYS', ...args]; -} +// export function transformArguments(args: Array): RedisCommandArguments { +// return ['COMMAND', 'GETKEYS', ...args]; +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts index 96b28186ccd..d1232d1999e 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts @@ -1,24 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments(args: Array): RedisCommandArguments { - return ['COMMAND', 'GETKEYSANDFLAGS', ...args]; -} +// export function transformArguments(args: Array): RedisCommandArguments { +// return ['COMMAND', 'GETKEYSANDFLAGS', ...args]; +// } -type KeysAndFlagsRawReply = Array<[ - RedisCommandArgument, - RedisCommandArguments -]>; +// type KeysAndFlagsRawReply = Array<[ +// RedisCommandArgument, +// RedisCommandArguments +// ]>; -type KeysAndFlagsReply = Array<{ - key: RedisCommandArgument; - flags: RedisCommandArguments; -}>; +// type KeysAndFlagsReply = Array<{ +// key: RedisCommandArgument; +// flags: RedisCommandArguments; +// }>; -export function transformReply(reply: KeysAndFlagsRawReply): KeysAndFlagsReply { - return reply.map(([key, flags]) => ({ - key, - flags - })); -} +// export function transformReply(reply: KeysAndFlagsRawReply): KeysAndFlagsReply { +// return reply.map(([key, flags]) => ({ +// key, +// flags +// })); +// } diff --git a/packages/client/lib/commands/COMMAND_INFO.ts b/packages/client/lib/commands/COMMAND_INFO.ts index 6f84d0edaf9..9de86b3f790 100644 --- a/packages/client/lib/commands/COMMAND_INFO.ts +++ b/packages/client/lib/commands/COMMAND_INFO.ts @@ -1,12 +1,12 @@ -import { RedisCommandArguments } from '.'; -import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; +// import { RedisCommandArguments } from '.'; +// import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments(commands: Array): RedisCommandArguments { - return ['COMMAND', 'INFO', ...commands]; -} +// export function transformArguments(commands: Array): RedisCommandArguments { +// return ['COMMAND', 'INFO', ...commands]; +// } -export function transformReply(reply: Array): Array { - return reply.map(command => command ? transformCommandReply(command) : null); -} +// export function transformReply(reply: Array): Array { +// return reply.map(command => command ? transformCommandReply(command) : null); +// } diff --git a/packages/client/lib/commands/COMMAND_LIST.ts b/packages/client/lib/commands/COMMAND_LIST.ts index a197bd1a4c6..eabf9856dd2 100644 --- a/packages/client/lib/commands/COMMAND_LIST.ts +++ b/packages/client/lib/commands/COMMAND_LIST.ts @@ -1,31 +1,31 @@ -import { RedisCommandArguments } from '.'; +// import { RedisCommandArguments } from '.'; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export enum FilterBy { - MODULE = 'MODULE', - ACLCAT = 'ACLCAT', - PATTERN = 'PATTERN' -} +// export enum FilterBy { +// MODULE = 'MODULE', +// ACLCAT = 'ACLCAT', +// PATTERN = 'PATTERN' +// } -interface Filter { - filterBy: FilterBy; - value: string; -} +// interface Filter { +// filterBy: FilterBy; +// value: string; +// } -export function transformArguments(filter?: Filter): RedisCommandArguments { - const args = ['COMMAND', 'LIST']; +// export function transformArguments(filter?: Filter): RedisCommandArguments { +// const args = ['COMMAND', 'LIST']; - if (filter) { - args.push( - 'FILTERBY', - filter.filterBy, - filter.value - ); - } +// if (filter) { +// args.push( +// 'FILTERBY', +// filter.filterBy, +// filter.value +// ); +// } - return args; -} +// return args; +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/CONFIG_GET.ts b/packages/client/lib/commands/CONFIG_GET.ts index 3afc0eddfd0..45b8e61ac4a 100644 --- a/packages/client/lib/commands/CONFIG_GET.ts +++ b/packages/client/lib/commands/CONFIG_GET.ts @@ -1,5 +1,10 @@ -export function transformArguments(parameter: string): Array { - return ['CONFIG', 'GET', parameter]; -} +import { RedisArgument, Command } from '../RESP/types'; +import { transformTuplesReply } from './generic-transformers'; -export { transformTuplesReply as transformReply } from './generic-transformers'; +export default { + IS_READ_ONLY: true, + transformArguments(parameter: RedisArgument) { + return ['CONFIG', 'GET', parameter]; + }, + transformReply: transformTuplesReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_RESETSTAT.ts b/packages/client/lib/commands/CONFIG_RESETSTAT.ts index aba54bc3c7b..119bbd6051b 100644 --- a/packages/client/lib/commands/CONFIG_RESETSTAT.ts +++ b/packages/client/lib/commands/CONFIG_RESETSTAT.ts @@ -1,5 +1,8 @@ -export function transformArguments(): Array { - return ['CONFIG', 'RESETSTAT']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + transformArguments() { + return ['CONFIG', 'RESETSTAT']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_REWRITE.ts b/packages/client/lib/commands/CONFIG_REWRITE.ts index 67984adf300..63f9acdb90e 100644 --- a/packages/client/lib/commands/CONFIG_REWRITE.ts +++ b/packages/client/lib/commands/CONFIG_REWRITE.ts @@ -1,5 +1,8 @@ -export function transformArguments(): Array { - return ['CONFIG', 'REWRITE']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + transformArguments() { + return ['CONFIG', 'REWRITE']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/CONFIG_SET.ts b/packages/client/lib/commands/CONFIG_SET.ts index 41f40d035d2..426a314fc17 100644 --- a/packages/client/lib/commands/CONFIG_SET.ts +++ b/packages/client/lib/commands/CONFIG_SET.ts @@ -1,23 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { SimpleStringReply, Command, RedisArgument } from '../RESP/types'; -type SingleParameter = [parameter: RedisCommandArgument, value: RedisCommandArgument]; +type SingleParameter = [parameter: RedisArgument, value: RedisArgument]; -type MultipleParameters = [config: Record]; +type MultipleParameters = [config: Record]; -export function transformArguments( +export default { + transformArguments( ...[parameterOrConfig, value]: SingleParameter | MultipleParameters -): RedisCommandArguments { - const args: RedisCommandArguments = ['CONFIG', 'SET']; - - if (typeof parameterOrConfig === 'string') { - args.push(parameterOrConfig, value!); + ) { + const args: Array = ['CONFIG', 'SET']; + + if (typeof parameterOrConfig === 'string' || Buffer.isBuffer(parameterOrConfig)) { + args.push(parameterOrConfig, value!); } else { - for (const [key, value] of Object.entries(parameterOrConfig)) { - args.push(key, value); - } + for (const [key, value] of Object.entries(parameterOrConfig)) { + args.push(key, value); + } } - + return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/COPY.ts b/packages/client/lib/commands/COPY.ts index b1e212a9956..2ef3c3224f8 100644 --- a/packages/client/lib/commands/COPY.ts +++ b/packages/client/lib/commands/COPY.ts @@ -1,28 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -interface CopyCommandOptions { - destinationDb?: number; - replace?: boolean; +export interface CopyCommandOptions { + DB?: number; + REPLACE?: boolean; } -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, - options?: CopyCommandOptions -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(source: RedisArgument, destination: RedisArgument, options?: CopyCommandOptions) { const args = ['COPY', source, destination]; - if (options?.destinationDb) { - args.push('DB', options.destinationDb.toString()); + if (options?.DB) { + args.push('DB', options.DB.toString()); } - if (options?.replace) { - args.push('REPLACE'); + if (options?.REPLACE) { + args.push('REPLACE'); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DBSIZE.ts b/packages/client/lib/commands/DBSIZE.ts index 6b442ec33a2..c4d3c8d857a 100644 --- a/packages/client/lib/commands/DBSIZE.ts +++ b/packages/client/lib/commands/DBSIZE.ts @@ -1,7 +1,9 @@ -export const IS_READ_ONLY = true; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + IS_READ_ONLY: true, + transformArguments() { return ['DBSIZE']; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DECR.ts b/packages/client/lib/commands/DECR.ts index 2b5f2c4bb5c..540148b7eed 100644 --- a/packages/client/lib/commands/DECR.ts +++ b/packages/client/lib/commands/DECR.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['DECR', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DECRBY.ts b/packages/client/lib/commands/DECRBY.ts index afe4d79f0a1..4a22aeb34cd 100644 --- a/packages/client/lib/commands/DECRBY.ts +++ b/packages/client/lib/commands/DECRBY.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - decrement: number -): RedisCommandArguments { - return ['DECRBY', key, decrement.toString()]; -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, decrement: number) { + return ['DECR', key, decrement.toString()]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DEL.ts b/packages/client/lib/commands/DEL.ts index d60abe0f28e..a9b10194279 100644 --- a/packages/client/lib/commands/DEL.ts +++ b/packages/client/lib/commands/DEL.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['DEL'], keys); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(keys: RedisVariadicArgument) { + return pushVariadicArguments(['DEL'], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DISCARD.ts b/packages/client/lib/commands/DISCARD.ts index acad8a722e1..e153070c989 100644 --- a/packages/client/lib/commands/DISCARD.ts +++ b/packages/client/lib/commands/DISCARD.ts @@ -1,7 +1,8 @@ -import { RedisCommandArgument } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + transformArguments() { return ['DISCARD']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/DUMP.ts b/packages/client/lib/commands/DUMP.ts index fd4354db45c..06b12f06d09 100644 --- a/packages/client/lib/commands/DUMP.ts +++ b/packages/client/lib/commands/DUMP.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['DUMP', key]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ECHO.ts b/packages/client/lib/commands/ECHO.ts index 7a837307e2b..828ab0382e2 100644 --- a/packages/client/lib/commands/ECHO.ts +++ b/packages/client/lib/commands/ECHO.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(message: RedisCommandArgument): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(message: RedisArgument) { return ['ECHO', message]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXISTS.ts b/packages/client/lib/commands/EXISTS.ts index 3bbc72ada46..a077943b8d2 100644 --- a/packages/client/lib/commands/EXISTS.ts +++ b/packages/client/lib/commands/EXISTS.ts @@ -1,14 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['EXISTS'], keys); -} - -export declare function transformReply(): number; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(keys: RedisVariadicArgument) { + return pushVariadicArguments(['EXISTS'], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIRE.ts b/packages/client/lib/commands/EXPIRE.ts index d9e85595c3b..3e57769bd6e 100644 --- a/packages/client/lib/commands/EXPIRE.ts +++ b/packages/client/lib/commands/EXPIRE.ts @@ -1,19 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, seconds: number, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { + ) { const args = ['EXPIRE', key, seconds.toString()]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIREAT.ts b/packages/client/lib/commands/EXPIREAT.ts index 84e7760fe7f..b9b5986caca 100644 --- a/packages/client/lib/commands/EXPIREAT.ts +++ b/packages/client/lib/commands/EXPIREAT.ts @@ -1,24 +1,21 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformEXAT } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, timestamp: number | Date, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { - const args = [ - 'EXPIREAT', - key, - transformEXAT(timestamp) - ]; + ) { + const args = ['EXPIRE', key, transformEXAT(timestamp)]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/EXPIRETIME.ts b/packages/client/lib/commands/EXPIRETIME.ts index 8c1bb075995..d6ac35aeb3d 100644 --- a/packages/client/lib/commands/EXPIRETIME.ts +++ b/packages/client/lib/commands/EXPIRETIME.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['EXPIRETIME', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FAILOVER.ts b/packages/client/lib/commands/FAILOVER.ts index c31dbc063de..0c1e710d321 100644 --- a/packages/client/lib/commands/FAILOVER.ts +++ b/packages/client/lib/commands/FAILOVER.ts @@ -1,33 +1,36 @@ +import { SimpleStringReply, Command } from '../RESP/types'; + interface FailoverOptions { - TO?: { - host: string; - port: number; - FORCE?: true; - }; - ABORT?: true; - TIMEOUT?: number; + TO?: { + host: string; + port: number; + FORCE?: true; + }; + ABORT?: true; + TIMEOUT?: number; } -export function transformArguments(options?: FailoverOptions): Array { +export default { + transformArguments(options?: FailoverOptions) { const args = ['FAILOVER']; if (options?.TO) { - args.push('TO', options.TO.host, options.TO.port.toString()); + args.push('TO', options.TO.host, options.TO.port.toString()); - if (options.TO.FORCE) { - args.push('FORCE'); - } + if (options.TO.FORCE) { + args.push('FORCE'); + } } if (options?.ABORT) { - args.push('ABORT'); + args.push('ABORT'); } if (options?.TIMEOUT) { - args.push('TIMEOUT', options.TIMEOUT.toString()); + args.push('TIMEOUT', options.TIMEOUT.toString()); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FLUSHALL.ts b/packages/client/lib/commands/FLUSHALL.ts index 967096bb9bd..bc41e24e0d5 100644 --- a/packages/client/lib/commands/FLUSHALL.ts +++ b/packages/client/lib/commands/FLUSHALL.ts @@ -1,16 +1,21 @@ -export enum RedisFlushModes { - ASYNC = 'ASYNC', - SYNC = 'SYNC' -} +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(mode?: RedisFlushModes): Array { - const args = ['FLUSHALL']; +export const REDIS_FLUSH_MODES = { + ASYNC: 'ASYNC', + SYNC: 'SYNC' +} as const; + +export type RedisFlushModes = typeof REDIS_FLUSH_MODES[keyof typeof REDIS_FLUSH_MODES]; +export default { + transformArguments(mode?: RedisFlushModes) { + const args = ['FLUSHALL']; + if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FLUSHDB.ts b/packages/client/lib/commands/FLUSHDB.ts index 5b8060df9eb..3dade5c0c50 100644 --- a/packages/client/lib/commands/FLUSHDB.ts +++ b/packages/client/lib/commands/FLUSHDB.ts @@ -1,13 +1,15 @@ +import { SimpleStringReply, Command } from '../RESP/types'; import { RedisFlushModes } from './FLUSHALL'; -export function transformArguments(mode?: RedisFlushModes): Array { +export default { + transformArguments(mode?: RedisFlushModes) { const args = ['FLUSHDB']; - + if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_DELETE.ts b/packages/client/lib/commands/FUNCTION_DELETE.ts index 4aa6be40e12..c0f8794d980 100644 --- a/packages/client/lib/commands/FUNCTION_DELETE.ts +++ b/packages/client/lib/commands/FUNCTION_DELETE.ts @@ -1,7 +1,10 @@ -import { RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(library: string): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(library: RedisArgument) { return ['FUNCTION', 'DELETE', library]; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_DUMP.ts b/packages/client/lib/commands/FUNCTION_DUMP.ts index f608e078c27..8f6ff047fa7 100644 --- a/packages/client/lib/commands/FUNCTION_DUMP.ts +++ b/packages/client/lib/commands/FUNCTION_DUMP.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['FUNCTION', 'DUMP']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_FLUSH.ts b/packages/client/lib/commands/FUNCTION_FLUSH.ts index 143282de97f..ab238a5eb5e 100644 --- a/packages/client/lib/commands/FUNCTION_FLUSH.ts +++ b/packages/client/lib/commands/FUNCTION_FLUSH.ts @@ -1,13 +1,17 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { RedisFlushModes } from './FLUSHALL'; -export function transformArguments(mode?: 'ASYNC' | 'SYNC'): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(mode?: RedisFlushModes) { const args = ['FUNCTION', 'FLUSH']; - + if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_KILL.ts b/packages/client/lib/commands/FUNCTION_KILL.ts index 517272e8376..f452b0b80d9 100644 --- a/packages/client/lib/commands/FUNCTION_KILL.ts +++ b/packages/client/lib/commands/FUNCTION_KILL.ts @@ -1,7 +1,10 @@ -import { RedisCommandArguments } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['FUNCTION', 'KILL']; -} - -export declare function transformReply(): 'OK'; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_LIST.ts b/packages/client/lib/commands/FUNCTION_LIST.ts index d6a39dc726d..c6075139156 100644 --- a/packages/client/lib/commands/FUNCTION_LIST.ts +++ b/packages/client/lib/commands/FUNCTION_LIST.ts @@ -1,16 +1,16 @@ -import { RedisCommandArguments } from '.'; -import { FunctionListItemReply, FunctionListRawItemReply, transformFunctionListItemReply } from './generic-transformers'; +// import { RedisCommandArguments } from '.'; +// import { FunctionListItemReply, FunctionListRawItemReply, transformFunctionListItemReply } from './generic-transformers'; -export function transformArguments(pattern?: string): RedisCommandArguments { - const args = ['FUNCTION', 'LIST']; +// export function transformArguments(pattern?: string): RedisCommandArguments { +// const args = ['FUNCTION', 'LIST']; - if (pattern) { - args.push(pattern); - } +// if (pattern) { +// args.push(pattern); +// } - return args; -} +// return args; +// } -export function transformReply(reply: Array): Array { - return reply.map(transformFunctionListItemReply); -} +// export function transformReply(reply: Array): Array { +// return reply.map(transformFunctionListItemReply); +// } diff --git a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts index 0d763301e87..41fdae90ad4 100644 --- a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts +++ b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts @@ -1,26 +1,26 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformFunctionListArguments } from './FUNCTION_LIST'; -import { FunctionListItemReply, FunctionListRawItemReply, transformFunctionListItemReply } from './generic-transformers'; +// import { RedisCommandArguments } from '.'; +// import { transformArguments as transformFunctionListArguments } from './FUNCTION_LIST'; +// import { FunctionListItemReply, FunctionListRawItemReply, transformFunctionListItemReply } from './generic-transformers'; -export function transformArguments(pattern?: string): RedisCommandArguments { - const args = transformFunctionListArguments(pattern); - args.push('WITHCODE'); - return args; -} +// export function transformArguments(pattern?: string): RedisCommandArguments { +// const args = transformFunctionListArguments(pattern); +// args.push('WITHCODE'); +// return args; +// } -type FunctionListWithCodeRawItemReply = [ - ...FunctionListRawItemReply, - 'library_code', - string -]; +// type FunctionListWithCodeRawItemReply = [ +// ...FunctionListRawItemReply, +// 'library_code', +// string +// ]; -interface FunctionListWithCodeItemReply extends FunctionListItemReply { - libraryCode: string; -} +// interface FunctionListWithCodeItemReply extends FunctionListItemReply { +// libraryCode: string; +// } -export function transformReply(reply: Array): Array { - return reply.map(library => ({ - ...transformFunctionListItemReply(library as unknown as FunctionListRawItemReply), - libraryCode: library[7] - })); -} +// export function transformReply(reply: Array): Array { +// return reply.map(library => ({ +// ...transformFunctionListItemReply(library as unknown as FunctionListRawItemReply), +// libraryCode: library[7] +// })); +// } diff --git a/packages/client/lib/commands/FUNCTION_LOAD.ts b/packages/client/lib/commands/FUNCTION_LOAD.ts index 7ab58d58598..c24ee0e9f81 100644 --- a/packages/client/lib/commands/FUNCTION_LOAD.ts +++ b/packages/client/lib/commands/FUNCTION_LOAD.ts @@ -1,22 +1,22 @@ -import { RedisCommandArguments } from '.'; +// import { RedisCommandArguments } from '.'; -interface FunctionLoadOptions { - REPLACE?: boolean; -} +// interface FunctionLoadOptions { +// REPLACE?: boolean; +// } -export function transformArguments( - code: string, - options?: FunctionLoadOptions -): RedisCommandArguments { - const args = ['FUNCTION', 'LOAD']; +// export function transformArguments( +// code: string, +// options?: FunctionLoadOptions +// ): RedisCommandArguments { +// const args = ['FUNCTION', 'LOAD']; - if (options?.REPLACE) { - args.push('REPLACE'); - } +// if (options?.REPLACE) { +// args.push('REPLACE'); +// } - args.push(code); +// args.push(code); - return args; -} +// return args; +// } -export declare function transformReply(): string; +// export declare function transformReply(): string; diff --git a/packages/client/lib/commands/FUNCTION_RESTORE.ts b/packages/client/lib/commands/FUNCTION_RESTORE.ts index bc9c41e262d..92f87288c38 100644 --- a/packages/client/lib/commands/FUNCTION_RESTORE.ts +++ b/packages/client/lib/commands/FUNCTION_RESTORE.ts @@ -1,16 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export function transformArguments( - dump: RedisCommandArgument, - mode?: 'FLUSH' | 'APPEND' | 'REPLACE' -): RedisCommandArguments { - const args = ['FUNCTION', 'RESTORE', dump]; +// export function transformArguments( +// dump: RedisCommandArgument, +// mode?: 'FLUSH' | 'APPEND' | 'REPLACE' +// ): RedisCommandArguments { +// const args = ['FUNCTION', 'RESTORE', dump]; - if (mode) { - args.push(mode); - } +// if (mode) { +// args.push(mode); +// } - return args; -} +// return args; +// } -export declare function transformReply(): 'OK'; +// export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/FUNCTION_STATS.ts b/packages/client/lib/commands/FUNCTION_STATS.ts index bb5c957e78b..6175d7775ef 100644 --- a/packages/client/lib/commands/FUNCTION_STATS.ts +++ b/packages/client/lib/commands/FUNCTION_STATS.ts @@ -1,56 +1,87 @@ -import { RedisCommandArguments } from '.'; - -export function transformArguments(): RedisCommandArguments { - return ['FUNCTION', 'STATS']; -} - -type FunctionStatsRawReply = [ - 'running_script', - null | [ - 'name', - string, - 'command', - string, - 'duration_ms', - number - ], - 'engines', - Array // "flat tuples" (there is no way to type that) - // ...[string, [ - // 'libraries_count', - // number, - // 'functions_count', - // number - // ]] -]; - -interface FunctionStatsReply { - runningScript: null | { - name: string; - command: string; - durationMs: number; - }; - engines: Record; -} - -export function transformReply(reply: FunctionStatsRawReply): FunctionStatsReply { - const engines = Object.create(null); - for (let i = 0; i < reply[3].length; i++) { - engines[reply[3][i]] = { - librariesCount: reply[3][++i][1], - functionsCount: reply[3][i][3] - }; - } - - return { - runningScript: reply[1] === null ? null : { - name: reply[1][1], - command: reply[1][3], - durationMs: reply[1][5] - }, - engines - }; -} +// // import { RedisCommandArguments } from '.'; + +// // export function transformArguments(): RedisCommandArguments { +// // return ['FUNCTION', 'STATS']; +// // } + +// // type FunctionStatsRawReply = [ +// // 'running_script', +// // null | [ +// // 'name', +// // string, +// // 'command', +// // string, +// // 'duration_ms', +// // number +// // ], +// // 'engines', +// // Array // "flat tuples" (there is no way to type that) +// // // ...[string, [ +// // // 'libraries_count', +// // // number, +// // // 'functions_count', +// // // number +// // // ]] +// // ]; + +// // interface FunctionStatsReply { +// // runningScript: null | { +// // name: string; +// // command: string; +// // durationMs: number; +// // }; +// // engines: Record; +// // } + +// // export function transformReply(reply: FunctionStatsRawReply): FunctionStatsReply { +// // const engines = Object.create(null); +// // for (let i = 0; i < reply[3].length; i++) { +// // engines[reply[3][i]] = { +// // librariesCount: reply[3][++i][1], +// // functionsCount: reply[3][i][3] +// // }; +// // } + +// // return { +// // runningScript: reply[1] === null ? null : { +// // name: reply[1][1], +// // command: reply[1][3], +// // durationMs: reply[1][5] +// // }, +// // engines +// // }; +// // } + + +// // #!LUA name=math \n redis.register_function{ function_name = "square", callback = function(keys, args) return args[1] * args[1] end, flags = { "no-writes" } } + +// import { Command, TuplesToMapReply, BlobStringReply, NullReply, NumberReply, MapReply } from '../RESP/types'; + +// type FunctionStatsReply = TuplesToMapReply<[ +// [BlobStringReply<'running_script'>, NullReply | TuplesToMapReply<[ +// [BlobStringReply<'name'>, BlobStringReply], +// [BlobStringReply<'command'>, BlobStringReply], +// [BlobStringReply<'duration_ms'>, NumberReply] +// ]>], +// [BlobStringReply<'engines'>, MapReply, NumberReply], +// [BlobStringReply<'functions_count'>, NumberReply] +// ]>>] +// ]>; + +// export default { +// IS_READ_ONLY: true, +// FIRST_KEY_INDEX: undefined, +// transformArguments() { +// return ['FUNCTION', 'STATS']; +// }, +// transformReply: { +// 2: (reply) => { + +// }, +// 3: undefined as unknown as () => +// } +// } as const satisfies Command; diff --git a/packages/client/lib/commands/GEOADD.ts b/packages/client/lib/commands/GEOADD.ts index daccb0842e0..2a34b5d5607 100644 --- a/packages/client/lib/commands/GEOADD.ts +++ b/packages/client/lib/commands/GEOADD.ts @@ -1,53 +1,65 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { CommandArguments, RedisArgument, NumberReply, Command } from '../RESP/types'; import { GeoCoordinates } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - interface GeoMember extends GeoCoordinates { - member: RedisCommandArgument; -} - -interface NX { - NX?: true; + member: RedisArgument; } -interface XX { - XX?: true; +interface GeoAddOptions { + condition: 'NX' | 'XX'; + /** + * @deprecated Use `{ condition: 'NX' }` instead. + */ + NX?: boolean; + /** + * @deprecated Use `{ condition: 'XX' }` instead. + */ + XX?: boolean; + CH?: boolean; } -type SetGuards = NX | XX; - -interface GeoAddCommonOptions { - CH?: true; -} - -type GeoAddOptions = SetGuards & GeoAddCommonOptions; - -export function transformArguments( - key: RedisCommandArgument, toAdd: GeoMember | Array, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + toAdd: GeoMember | Array, options?: GeoAddOptions -): RedisCommandArguments { + ) { const args = ['GEOADD', key]; - if ((options as NX)?.NX) { - args.push('NX'); - } else if ((options as XX)?.XX) { - args.push('XX'); + if (options?.condition) { + args.push(options.condition); + } else if (options?.NX) { + args.push('NX'); + } else if (options?.XX) { + args.push('XX'); } if (options?.CH) { - args.push('CH'); + args.push('CH'); } - for (const { longitude, latitude, member } of (Array.isArray(toAdd) ? toAdd : [toAdd])) { - args.push( - longitude.toString(), - latitude.toString(), - member - ); + if (Array.isArray(toAdd)) { + for (const member of toAdd) { + pushMember(args, member); + } + } else { + pushMember(args, toAdd); } return args; -} + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; -export declare function transformReply(): number; +function pushMember( + args: CommandArguments, + { longitude, latitude, member }: GeoMember +) { + args.push( + longitude.toString(), + latitude.toString(), + member + ); +} diff --git a/packages/client/lib/commands/GEODIST.ts b/packages/client/lib/commands/GEODIST.ts index 5dbf8ece9cc..da6dee1b774 100644 --- a/packages/client/lib/commands/GEODIST.ts +++ b/packages/client/lib/commands/GEODIST.ts @@ -1,16 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { GeoUnits } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member1: RedisCommandArgument, - member2: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + member1: RedisArgument, + member2: RedisArgument, unit?: GeoUnits -): RedisCommandArguments { + ) { const args = ['GEODIST', key, member1, member2]; if (unit) { @@ -18,8 +17,8 @@ export function transformArguments( } return args; -} - -export function transformReply(reply: RedisCommandArgument | null): number | null { + }, + transformReply(reply: BlobStringReply | NullReply) { return reply === null ? null : Number(reply); -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEOHASH.ts b/packages/client/lib/commands/GEOHASH.ts index 55e22c497e2..d8d2732e512 100644 --- a/packages/client/lib/commands/GEOHASH.ts +++ b/packages/client/lib/commands/GEOHASH.ts @@ -1,15 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['GEOHASH', key], member); -} - -export declare function transformReply(): Array; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + member: RedisVariadicArgument + ) { + return pushVariadicArguments(['GEOHASH', key], member); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEOPOS.ts b/packages/client/lib/commands/GEOPOS.ts index 0a5f079deeb..d9fb5d3a25d 100644 --- a/packages/client/lib/commands/GEOPOS.ts +++ b/packages/client/lib/commands/GEOPOS.ts @@ -1,27 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { ArrayReply, BlobStringReply, NullReply, Command, RedisArgument } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['GEOPOS', key], member); -} - -type GeoCoordinatesRawReply = Array<[RedisCommandArgument, RedisCommandArgument] | null>; - -interface GeoCoordinates { - longitude: RedisCommandArgument; - latitude: RedisCommandArgument; -} - -export function transformReply(reply: GeoCoordinatesRawReply): Array { - return reply.map(coordinates => coordinates === null ? null : { - longitude: coordinates[0], - latitude: coordinates[1] +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + member: RedisVariadicArgument + ) { + return pushVariadicArguments(['GEOPOS', key], member); + }, + transformReply(reply: ArrayReply<[BlobStringReply, BlobStringReply] | NullReply>) { + return reply.map(item => item === null ? null : { + longitude: item[0], + latitude: item[1] }); -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/GEORADIUS.ts b/packages/client/lib/commands/GEORADIUS.ts index f47cf508848..4b5af7a078e 100644 --- a/packages/client/lib/commands/GEORADIUS.ts +++ b/packages/client/lib/commands/GEORADIUS.ts @@ -1,25 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchOptions, GeoCoordinates, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoSearchOptions, GeoCoordinates, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments( - key: RedisCommandArgument, - coordinates: GeoCoordinates, - radius: number, - unit: GeoUnits, - options?: GeoSearchOptions -): RedisCommandArguments { - return pushGeoRadiusArguments( - ['GEORADIUS'], - key, - coordinates, - radius, - unit, - options - ); -} +// export function transformArguments( +// key: RedisCommandArgument, +// coordinates: GeoCoordinates, +// radius: number, +// unit: GeoUnits, +// options?: GeoSearchOptions +// ): RedisCommandArguments { +// return pushGeoRadiusArguments( +// ['GEORADIUS'], +// key, +// coordinates, +// radius, +// unit, +// options +// ); +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER.ts index 96bb622fb85..ab6950ac8db 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER.ts @@ -1,25 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchOptions, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoSearchOptions, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments( - key: RedisCommandArgument, - member: string, - radius: number, - unit: GeoUnits, - options?: GeoSearchOptions -): RedisCommandArguments { - return pushGeoRadiusArguments( - ['GEORADIUSBYMEMBER'], - key, - member, - radius, - unit, - options - ); -} +// export function transformArguments( +// key: RedisCommandArgument, +// member: string, +// radius: number, +// unit: GeoUnits, +// options?: GeoSearchOptions +// ): RedisCommandArguments { +// return pushGeoRadiusArguments( +// ['GEORADIUSBYMEMBER'], +// key, +// member, +// radius, +// unit, +// options +// ); +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts b/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts index 28f3c25fac9..ea1674cf3fc 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBERSTORE.ts @@ -1,25 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoUnits, GeoRadiusStoreOptions, pushGeoRadiusStoreArguments } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoUnits, GeoRadiusStoreOptions, pushGeoRadiusStoreArguments } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER'; -export function transformArguments( - key: RedisCommandArgument, - member: string, - radius: number, - unit: GeoUnits, - destination: RedisCommandArgument, - options?: GeoRadiusStoreOptions, -): RedisCommandArguments { - return pushGeoRadiusStoreArguments( - ['GEORADIUSBYMEMBER'], - key, - member, - radius, - unit, - destination, - options - ); -} +// export function transformArguments( +// key: RedisCommandArgument, +// member: string, +// radius: number, +// unit: GeoUnits, +// destination: RedisCommandArgument, +// options?: GeoRadiusStoreOptions, +// ): RedisCommandArguments { +// return pushGeoRadiusStoreArguments( +// ['GEORADIUSBYMEMBER'], +// key, +// member, +// radius, +// unit, +// destination, +// options +// ); +// } -export declare function transformReply(): number +// export declare function transformReply(): number diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts index 63f29ae65b5..1ad9450ae38 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO.ts @@ -1,25 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchOptions, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoSearchOptions, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments( - key: RedisCommandArgument, - member: string, - radius: number, - unit: GeoUnits, - options?: GeoSearchOptions -): RedisCommandArguments { - return pushGeoRadiusArguments( - ['GEORADIUSBYMEMBER_RO'], - key, - member, - radius, - unit, - options - ); -} +// export function transformArguments( +// key: RedisCommandArgument, +// member: string, +// radius: number, +// unit: GeoUnits, +// options?: GeoSearchOptions +// ): RedisCommandArguments { +// return pushGeoRadiusArguments( +// ['GEORADIUSBYMEMBER_RO'], +// key, +// member, +// radius, +// unit, +// options +// ); +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts index 6061be734b5..116bb9200fa 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_RO_WITH.ts @@ -1,30 +1,30 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoReplyWith, GeoSearchOptions, GeoUnits } from './generic-transformers'; -import { transformArguments as geoRadiusTransformArguments } from './GEORADIUSBYMEMBER_RO'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoReplyWith, GeoSearchOptions, GeoUnits } from './generic-transformers'; +// import { transformArguments as geoRadiusTransformArguments } from './GEORADIUSBYMEMBER_RO'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER_RO'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER_RO'; -export function transformArguments( - key: RedisCommandArgument, - member: string, - radius: number, - unit: GeoUnits, - replyWith: Array, - options?: GeoSearchOptions -): RedisCommandArguments { - const args: RedisCommandArguments = geoRadiusTransformArguments( - key, - member, - radius, - unit, - options - ); +// export function transformArguments( +// key: RedisCommandArgument, +// member: string, +// radius: number, +// unit: GeoUnits, +// replyWith: Array, +// options?: GeoSearchOptions +// ): RedisCommandArguments { +// const args: RedisCommandArguments = geoRadiusTransformArguments( +// key, +// member, +// radius, +// unit, +// options +// ); - args.push(...replyWith); +// args.push(...replyWith); - args.preserve = replyWith; +// args.preserve = replyWith; - return args; -} +// return args; +// } -export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; +// export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts index 7d7dbe06a54..09bb5bbbb44 100644 --- a/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts +++ b/packages/client/lib/commands/GEORADIUSBYMEMBER_WITH.ts @@ -1,30 +1,30 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoReplyWith, GeoSearchOptions, GeoUnits } from './generic-transformers'; -import { transformArguments as transformGeoRadiusArguments } from './GEORADIUSBYMEMBER'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoReplyWith, GeoSearchOptions, GeoUnits } from './generic-transformers'; +// import { transformArguments as transformGeoRadiusArguments } from './GEORADIUSBYMEMBER'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUSBYMEMBER'; -export function transformArguments( - key: RedisCommandArgument, - member: string, - radius: number, - unit: GeoUnits, - replyWith: Array, - options?: GeoSearchOptions -): RedisCommandArguments { - const args: RedisCommandArguments = transformGeoRadiusArguments( - key, - member, - radius, - unit, - options - ); +// export function transformArguments( +// key: RedisCommandArgument, +// member: string, +// radius: number, +// unit: GeoUnits, +// replyWith: Array, +// options?: GeoSearchOptions +// ): RedisCommandArguments { +// const args: RedisCommandArguments = transformGeoRadiusArguments( +// key, +// member, +// radius, +// unit, +// options +// ); - args.push(...replyWith); +// args.push(...replyWith); - args.preserve = replyWith; +// args.preserve = replyWith; - return args; -} +// return args; +// } -export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; +// export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GEORADIUSSTORE.ts b/packages/client/lib/commands/GEORADIUSSTORE.ts index ad2317aa3af..03445259648 100644 --- a/packages/client/lib/commands/GEORADIUSSTORE.ts +++ b/packages/client/lib/commands/GEORADIUSSTORE.ts @@ -1,25 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoCoordinates, GeoUnits, GeoRadiusStoreOptions, pushGeoRadiusStoreArguments } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoCoordinates, GeoUnits, GeoRadiusStoreOptions, pushGeoRadiusStoreArguments } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS'; -export function transformArguments( - key: RedisCommandArgument, - coordinates: GeoCoordinates, - radius: number, - unit: GeoUnits, - destination: RedisCommandArgument, - options?: GeoRadiusStoreOptions, -): RedisCommandArguments { - return pushGeoRadiusStoreArguments( - ['GEORADIUS'], - key, - coordinates, - radius, - unit, - destination, - options - ); -} +// export function transformArguments( +// key: RedisCommandArgument, +// coordinates: GeoCoordinates, +// radius: number, +// unit: GeoUnits, +// destination: RedisCommandArgument, +// options?: GeoRadiusStoreOptions, +// ): RedisCommandArguments { +// return pushGeoRadiusStoreArguments( +// ['GEORADIUS'], +// key, +// coordinates, +// radius, +// unit, +// destination, +// options +// ); +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/GEORADIUS_RO.ts b/packages/client/lib/commands/GEORADIUS_RO.ts index ac378a5150b..d9c01007ed9 100644 --- a/packages/client/lib/commands/GEORADIUS_RO.ts +++ b/packages/client/lib/commands/GEORADIUS_RO.ts @@ -1,25 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchOptions, GeoCoordinates, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoSearchOptions, GeoCoordinates, pushGeoRadiusArguments, GeoUnits } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments( - key: RedisCommandArgument, - coordinates: GeoCoordinates, - radius: number, - unit: GeoUnits, - options?: GeoSearchOptions -): RedisCommandArguments { - return pushGeoRadiusArguments( - ['GEORADIUS_RO'], - key, - coordinates, - radius, - unit, - options - ); -} +// export function transformArguments( +// key: RedisCommandArgument, +// coordinates: GeoCoordinates, +// radius: number, +// unit: GeoUnits, +// options?: GeoSearchOptions +// ): RedisCommandArguments { +// return pushGeoRadiusArguments( +// ['GEORADIUS_RO'], +// key, +// coordinates, +// radius, +// unit, +// options +// ); +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEORADIUS_RO_WITH.ts b/packages/client/lib/commands/GEORADIUS_RO_WITH.ts index 424e5fcd998..156f5cfa9f4 100644 --- a/packages/client/lib/commands/GEORADIUS_RO_WITH.ts +++ b/packages/client/lib/commands/GEORADIUS_RO_WITH.ts @@ -1,30 +1,30 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoReplyWith, GeoSearchOptions, GeoCoordinates, GeoUnits } from './generic-transformers'; -import { transformArguments as transformGeoRadiusRoArguments } from './GEORADIUS_RO'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoReplyWith, GeoSearchOptions, GeoCoordinates, GeoUnits } from './generic-transformers'; +// import { transformArguments as transformGeoRadiusRoArguments } from './GEORADIUS_RO'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS_RO'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS_RO'; -export function transformArguments( - key: RedisCommandArgument, - coordinates: GeoCoordinates, - radius: number, - unit: GeoUnits, - replyWith: Array, - options?: GeoSearchOptions -): RedisCommandArguments { - const args: RedisCommandArguments = transformGeoRadiusRoArguments( - key, - coordinates, - radius, - unit, - options - ); +// export function transformArguments( +// key: RedisCommandArgument, +// coordinates: GeoCoordinates, +// radius: number, +// unit: GeoUnits, +// replyWith: Array, +// options?: GeoSearchOptions +// ): RedisCommandArguments { +// const args: RedisCommandArguments = transformGeoRadiusRoArguments( +// key, +// coordinates, +// radius, +// unit, +// options +// ); - args.push(...replyWith); +// args.push(...replyWith); - args.preserve = replyWith; +// args.preserve = replyWith; - return args; -} +// return args; +// } -export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; +// export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GEORADIUS_WITH.ts b/packages/client/lib/commands/GEORADIUS_WITH.ts index dc3f4288f01..d41132ce2d6 100644 --- a/packages/client/lib/commands/GEORADIUS_WITH.ts +++ b/packages/client/lib/commands/GEORADIUS_WITH.ts @@ -1,30 +1,30 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoReplyWith, GeoSearchOptions, GeoCoordinates, GeoUnits } from './generic-transformers'; -import { transformArguments as transformGeoRadiusArguments } from './GEORADIUS'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoReplyWith, GeoSearchOptions, GeoCoordinates, GeoUnits } from './generic-transformers'; +// import { transformArguments as transformGeoRadiusArguments } from './GEORADIUS'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEORADIUS'; -export function transformArguments( - key: RedisCommandArgument, - coordinates: GeoCoordinates, - radius: number, - unit: GeoUnits, - replyWith: Array, - options?: GeoSearchOptions -): RedisCommandArguments { - const args: RedisCommandArguments = transformGeoRadiusArguments( - key, - coordinates, - radius, - unit, - options - ); +// export function transformArguments( +// key: RedisCommandArgument, +// coordinates: GeoCoordinates, +// radius: number, +// unit: GeoUnits, +// replyWith: Array, +// options?: GeoSearchOptions +// ): RedisCommandArguments { +// const args: RedisCommandArguments = transformGeoRadiusArguments( +// key, +// coordinates, +// radius, +// unit, +// options +// ); - args.push(...replyWith); +// args.push(...replyWith); - args.preserve = replyWith; +// args.preserve = replyWith; - return args; -} +// return args; +// } -export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; +// export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GEOSEARCH.ts b/packages/client/lib/commands/GEOSEARCH.ts index a02a21391f6..31a65ee8dce 100644 --- a/packages/client/lib/commands/GEOSEARCH.ts +++ b/packages/client/lib/commands/GEOSEARCH.ts @@ -1,17 +1,17 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments( - key: RedisCommandArgument, - from: GeoSearchFrom, - by: GeoSearchBy, - options?: GeoSearchOptions -): RedisCommandArguments { - return pushGeoSearchArguments(['GEOSEARCH'], key, from, by, options); -} +// export function transformArguments( +// key: RedisCommandArgument, +// from: GeoSearchFrom, +// by: GeoSearchBy, +// options?: GeoSearchOptions +// ): RedisCommandArguments { +// return pushGeoSearchArguments(['GEOSEARCH'], key, from, by, options); +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/GEOSEARCHSTORE.ts b/packages/client/lib/commands/GEOSEARCHSTORE.ts index 7a91450cd9e..a8cdb82d71b 100644 --- a/packages/client/lib/commands/GEOSEARCHSTORE.ts +++ b/packages/client/lib/commands/GEOSEARCHSTORE.ts @@ -1,38 +1,38 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoSearchFrom, GeoSearchBy, GeoSearchOptions, pushGeoSearchArguments } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; -interface GeoSearchStoreOptions extends GeoSearchOptions { - STOREDIST?: true; -} +// interface GeoSearchStoreOptions extends GeoSearchOptions { +// STOREDIST?: true; +// } -export function transformArguments( - destination: RedisCommandArgument, - source: RedisCommandArgument, - from: GeoSearchFrom, - by: GeoSearchBy, - options?: GeoSearchStoreOptions -): RedisCommandArguments { - const args = pushGeoSearchArguments( - ['GEOSEARCHSTORE', destination], - source, - from, - by, - options - ); +// export function transformArguments( +// destination: RedisCommandArgument, +// source: RedisCommandArgument, +// from: GeoSearchFrom, +// by: GeoSearchBy, +// options?: GeoSearchStoreOptions +// ): RedisCommandArguments { +// const args = pushGeoSearchArguments( +// ['GEOSEARCHSTORE', destination], +// source, +// from, +// by, +// options +// ); - if (options?.STOREDIST) { - args.push('STOREDIST'); - } +// if (options?.STOREDIST) { +// args.push('STOREDIST'); +// } - return args; -} +// return args; +// } -export function transformReply(reply: number): number { - if (typeof reply !== 'number') { - throw new TypeError(`https://github.com/redis/redis/issues/9261`); - } +// export function transformReply(reply: number): number { +// if (typeof reply !== 'number') { +// throw new TypeError(`https://github.com/redis/redis/issues/9261`); +// } - return reply; -} +// return reply; +// } diff --git a/packages/client/lib/commands/GEOSEARCH_WITH.ts b/packages/client/lib/commands/GEOSEARCH_WITH.ts index d7a5f456a94..e706b08b9e8 100644 --- a/packages/client/lib/commands/GEOSEARCH_WITH.ts +++ b/packages/client/lib/commands/GEOSEARCH_WITH.ts @@ -1,23 +1,23 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions } from './generic-transformers'; -import { transformArguments as geoSearchTransformArguments } from './GEOSEARCH'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { GeoSearchFrom, GeoSearchBy, GeoReplyWith, GeoSearchOptions } from './generic-transformers'; +// import { transformArguments as geoSearchTransformArguments } from './GEOSEARCH'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './GEOSEARCH'; -export function transformArguments( - key: RedisCommandArgument, - from: GeoSearchFrom, - by: GeoSearchBy, - replyWith: Array, - options?: GeoSearchOptions -): RedisCommandArguments { - const args: RedisCommandArguments = geoSearchTransformArguments(key, from, by, options); +// export function transformArguments( +// key: RedisCommandArgument, +// from: GeoSearchFrom, +// by: GeoSearchBy, +// replyWith: Array, +// options?: GeoSearchOptions +// ): RedisCommandArguments { +// const args: RedisCommandArguments = geoSearchTransformArguments(key, from, by, options); - args.push(...replyWith); +// args.push(...replyWith); - args.preserve = replyWith; +// args.preserve = replyWith; - return args; -} +// return args; +// } -export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; +// export { transformGeoMembersWithReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/GET.ts b/packages/client/lib/commands/GET.ts index 127b0a56349..bb3db4f76d9 100644 --- a/packages/client/lib/commands/GET.ts +++ b/packages/client/lib/commands/GET.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['GET', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETBIT.ts b/packages/client/lib/commands/GETBIT.ts index 67f67f39b19..d8ece8f523a 100644 --- a/packages/client/lib/commands/GETBIT.ts +++ b/packages/client/lib/commands/GETBIT.ts @@ -1,15 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; import { BitValue } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - offset: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, offset: number) { return ['GETBIT', key, offset.toString()]; -} - -export declare function transformReply(): BitValue; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETDEL.ts b/packages/client/lib/commands/GETDEL.ts index 2d91e6cc025..c11fd047df4 100644 --- a/packages/client/lib/commands/GETDEL.ts +++ b/packages/client/lib/commands/GETDEL.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['GETDEL', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETEX.ts b/packages/client/lib/commands/GETEX.ts index 5b3cec6d887..201a888208a 100644 --- a/packages/client/lib/commands/GETEX.ts +++ b/packages/client/lib/commands/GETEX.ts @@ -1,39 +1,37 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { transformEXAT, transformPXAT } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -type GetExModes = { - EX: number; +export type GetExModes = { + EX: number; } | { - PX: number; + PX: number; } | { - EXAT: number | Date; + EXAT: number | Date; } | { - PXAT: number | Date; + PXAT: number | Date; } | { - PERSIST: true; + PERSIST: true; }; -export function transformArguments( - key: RedisCommandArgument, - mode: GetExModes -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, mode: GetExModes) { const args = ['GETEX', key]; if ('EX' in mode) { - args.push('EX', mode.EX.toString()); + args.push('EX', mode.EX.toString()); } else if ('PX' in mode) { - args.push('PX', mode.PX.toString()); + args.push('PX', mode.PX.toString()); } else if ('EXAT' in mode) { - args.push('EXAT', transformEXAT(mode.EXAT)); + args.push('EXAT', transformEXAT(mode.EXAT)); } else if ('PXAT' in mode) { - args.push('PXAT', transformPXAT(mode.PXAT)); + args.push('PXAT', transformPXAT(mode.PXAT)); } else { // PERSIST - args.push('PERSIST'); + args.push('PERSIST'); } return args; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETRANGE.ts b/packages/client/lib/commands/GETRANGE.ts index 2d12d937cc6..e5357cd120b 100644 --- a/packages/client/lib/commands/GETRANGE.ts +++ b/packages/client/lib/commands/GETRANGE.ts @@ -1,15 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - start: number, - end: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, start: number, end: number) { return ['GETRANGE', key, start.toString(), end.toString()]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/GETSET.ts b/packages/client/lib/commands/GETSET.ts index 87d111792c6..bbe920181b2 100644 --- a/packages/client/lib/commands/GETSET.ts +++ b/packages/client/lib/commands/GETSET.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, value: RedisArgument) { return ['GETSET', key, value]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HDEL.ts b/packages/client/lib/commands/HDEL.ts index 1a994e109d6..64aa55edda6 100644 --- a/packages/client/lib/commands/HDEL.ts +++ b/packages/client/lib/commands/HDEL.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['HDEL', key], field); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, field: RedisVariadicArgument) { + return pushVariadicArguments(['HDEL', key], field); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HELLO.ts b/packages/client/lib/commands/HELLO.ts index d943f2e4c35..d4784b67c40 100644 --- a/packages/client/lib/commands/HELLO.ts +++ b/packages/client/lib/commands/HELLO.ts @@ -1,65 +1,54 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { AuthOptions } from './AUTH'; - -interface HelloOptions { - protover: number; - auth?: Required; - clientName?: string; +import { RedisArgument, ArrayReply, BlobStringReply, Command, NumberReply, Resp2Reply, RespVersions, TuplesToMapReply } from '../RESP/types'; + +export interface HelloOptions { + AUTH?: { + username: RedisArgument; + password: RedisArgument; + }; + SETNAME?: string; } -export function transformArguments(options?: HelloOptions): RedisCommandArguments { - const args: RedisCommandArguments = ['HELLO']; - - if (options) { - args.push(options.protover.toString()); - - if (options.auth) { - args.push('AUTH', options.auth.username, options.auth.password); - } - - if (options.clientName) { - args.push('SETNAME', options.clientName); - } +export type HelloReply = TuplesToMapReply<[ + [BlobStringReply<'server'>, BlobStringReply], + [BlobStringReply<'version'>, BlobStringReply], + [BlobStringReply<'proto'>, NumberReply], + [BlobStringReply<'id'>, NumberReply], + [BlobStringReply<'mode'>, BlobStringReply], + [BlobStringReply<'role'>, BlobStringReply], + [BlobStringReply<'modules'>, ArrayReply] +]>; + +export default { + transformArguments(protoover: RespVersions, options?: HelloOptions) { + const args: Array = ['HELLO', protoover.toString()]; + + if (options?.AUTH) { + args.push( + 'AUTH', + options.AUTH.username, + options.AUTH.password + ); } + if (options?.SETNAME) { + args.push( + 'SETNAME', + options.SETNAME + ); + } + return args; -} - -type HelloRawReply = [ - _: never, - server: RedisCommandArgument, - _: never, - version: RedisCommandArgument, - _: never, - proto: number, - _: never, - id: number, - _: never, - mode: RedisCommandArgument, - _: never, - role: RedisCommandArgument, - _: never, - modules: Array -]; - -interface HelloTransformedReply { - server: RedisCommandArgument; - version: RedisCommandArgument; - proto: number; - id: number; - mode: RedisCommandArgument; - role: RedisCommandArgument; - modules: Array; -} - -export function transformReply(reply: HelloRawReply): HelloTransformedReply { - return { - server: reply[1], - version: reply[3], - proto: reply[5], - id: reply[7], - mode: reply[9], - role: reply[11], - modules: reply[13] - }; -} + }, + transformReply: { + 2: (reply: Resp2Reply) => ({ + server: reply[1], + version: reply[3], + proto: reply[5], + id: reply[7], + mode: reply[9], + role: reply[11], + modules: reply[13] + }), + 3: undefined as unknown as () => HelloReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/HEXISTS.ts b/packages/client/lib/commands/HEXISTS.ts index 289be20aa83..dc7e937ee78 100644 --- a/packages/client/lib/commands/HEXISTS.ts +++ b/packages/client/lib/commands/HEXISTS.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, field: RedisArgument) { return ['HEXISTS', key, field]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply<0 | 1> +} as const satisfies Command; diff --git a/packages/client/lib/commands/HGET.ts b/packages/client/lib/commands/HGET.ts index fcfd31e6172..d83f84e24fa 100644 --- a/packages/client/lib/commands/HGET.ts +++ b/packages/client/lib/commands/HGET.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, field: RedisArgument) { return ['HGET', key, field]; -} - -export declare function transformReply(): RedisCommandArgument | undefined; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HGETALL.ts b/packages/client/lib/commands/HGETALL.ts index bf51760ff0e..f1f0ac50bcb 100644 --- a/packages/client/lib/commands/HGETALL.ts +++ b/packages/client/lib/commands/HGETALL.ts @@ -1,13 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, MapReply, BlobStringReply, Command } from '../RESP/types'; +import { transformTuplesReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export const TRANSFORM_LEGACY_REPLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HGETALL', key]; -} - -export { transformTuplesReply as transformReply } from './generic-transformers'; + }, + TRANSFORM_LEGACY_REPLY: true, + transformReply: { + 2: transformTuplesReply, + 3: undefined as unknown as () => MapReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/HINCRBY.ts b/packages/client/lib/commands/HINCRBY.ts index b2cf6eefe89..cb7f62ebef5 100644 --- a/packages/client/lib/commands/HINCRBY.ts +++ b/packages/client/lib/commands/HINCRBY.ts @@ -1,13 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + field: RedisArgument, increment: number -): RedisCommandArguments { - return ['HINCRBY', key, field, increment.toString()]; -} - -export declare function transformReply(): number; + ) { + return [ + 'HINCRBY', + key, + field, + increment.toString() + ]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HINCRBYFLOAT.ts b/packages/client/lib/commands/HINCRBYFLOAT.ts index 0e2de6e9b29..a4eea75c827 100644 --- a/packages/client/lib/commands/HINCRBYFLOAT.ts +++ b/packages/client/lib/commands/HINCRBYFLOAT.ts @@ -1,13 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + field: RedisArgument, increment: number -): RedisCommandArguments { - return ['HINCRBYFLOAT', key, field, increment.toString()]; -} - -export declare function transformReply(): number; + ) { + return [ + 'HINCRBYFLOAT', + key, + field, + increment.toString() + ]; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HKEYS.ts b/packages/client/lib/commands/HKEYS.ts index 3d629733d0e..00af43f7a40 100644 --- a/packages/client/lib/commands/HKEYS.ts +++ b/packages/client/lib/commands/HKEYS.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HKEYS', key]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HLEN.ts b/packages/client/lib/commands/HLEN.ts index 15a93d408d7..8f156d303e2 100644 --- a/packages/client/lib/commands/HLEN.ts +++ b/packages/client/lib/commands/HLEN.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HMGET.ts b/packages/client/lib/commands/HMGET.ts index 64b4014abeb..1c4acafdeef 100644 --- a/packages/client/lib/commands/HMGET.ts +++ b/packages/client/lib/commands/HMGET.ts @@ -1,15 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - fields: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['HMGET', key], fields); -} - -export declare function transformReply(): Array; +import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + fields: RedisVariadicArgument + ) { + return pushVariadicArguments(['HMGET'], fields); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD.ts b/packages/client/lib/commands/HRANDFIELD.ts index a2c70aabd52..be878e244a0 100644 --- a/packages/client/lib/commands/HRANDFIELD.ts +++ b/packages/client/lib/commands/HRANDFIELD.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HRANDFIELD', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT.ts b/packages/client/lib/commands/HRANDFIELD_COUNT.ts index 01b8df63273..4b6f42a115b 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT.ts @@ -1,16 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformHRandFieldArguments } from './HRANDFIELD'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HRANDFIELD'; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformHRandFieldArguments(key), - count.toString() - ]; -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, count: number) { + return ['HRANDFIELD', key, count.toString()]; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts deleted file mode 100644 index c4e6409a726..00000000000 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './HRANDFIELD_COUNT_WITHVALUES'; - -describe('HRANDFIELD COUNT WITHVALUES', () => { - testUtils.isVersionGreaterThanHook([6, 2, 5]); - - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['HRANDFIELD', 'key', '1', 'WITHVALUES'] - ); - }); - - testUtils.testWithClient('client.hRandFieldCountWithValues', async client => { - assert.deepEqual( - await client.hRandFieldCountWithValues('key', 1), - Object.create(null) - ); - }, GLOBAL.SERVERS.OPEN); -}); diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts index 3e09dbb9a14..2c7b17d5761 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -1,16 +1,36 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformHRandFieldCountArguments } from './HRANDFIELD_COUNT'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HRANDFIELD_COUNT'; +export type HRandFieldCountWithValuesReply = Array<{ + field: BlobStringReply; + value: BlobStringReply; +}>; -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformHRandFieldCountArguments(key, count), - 'WITHVALUES' - ]; -} +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, count: number) { + return ['HRANDFIELD', key, count.toString(), 'WITHVALUES']; + }, + transformReply: { + 2: (rawReply: ArrayReply) => { + const reply: HRandFieldCountWithValuesReply = []; -export { transformTuplesReply as transformReply } from './generic-transformers'; + let i = 0; + while (i < rawReply.length) { + reply.push({ + field: rawReply[i++], + value: rawReply[i++] + }); + } + + return reply; + }, + 3: (reply: ArrayReply<[BlobStringReply, BlobStringReply]>) => { + return reply.map(([field, value]) => ({ + field, + value + })) satisfies HRandFieldCountWithValuesReply; + } + } +} as const satisfies Command; + \ No newline at end of file diff --git a/packages/client/lib/commands/HSCAN.ts b/packages/client/lib/commands/HSCAN.ts index ba18fb986bc..2391424eb21 100644 --- a/packages/client/lib/commands/HSCAN.ts +++ b/packages/client/lib/commands/HSCAN.ts @@ -1,44 +1,34 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ScanOptions, pushScanArguments } from './generic-transformers'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; +import { ScanCommonOptions, pushScanArguments } from './SCAN'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - cursor: number, - options?: ScanOptions -): RedisCommandArguments { - return pushScanArguments([ - 'HSCAN', - key - ], cursor, options); -} - -type HScanRawReply = [RedisCommandArgument, Array]; - -export interface HScanTuple { - field: RedisCommandArgument; - value: RedisCommandArgument; -} - -interface HScanReply { - cursor: number; - tuples: Array; +export interface HScanEntry { + field: BlobStringReply; + value: BlobStringReply; } -export function transformReply([cursor, rawTuples]: HScanRawReply): HScanReply { - const parsedTuples = []; - for (let i = 0; i < rawTuples.length; i += 2) { - parsedTuples.push({ - field: rawTuples[i], - value: rawTuples[i + 1] - }); +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + cursor: number, + options?: ScanCommonOptions + ) { + return pushScanArguments(['HSCAN', key], cursor, options); + }, + transformReply([cursor, rawEntries]: [BlobStringReply, Array]) { + const entries = []; + let i = 0; + while (i < rawEntries.length) { + entries.push({ + field: rawEntries[i++], + value: rawEntries[i++] + } satisfies HScanEntry); } return { - cursor: Number(cursor), - tuples: parsedTuples + cursor: Number(cursor), + entries }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/HSET.ts b/packages/client/lib/commands/HSET.ts index 261ef98c779..7cb98c827bd 100644 --- a/packages/client/lib/commands/HSET.ts +++ b/packages/client/lib/commands/HSET.ts @@ -1,73 +1,75 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; +export type HashTypes = RedisArgument | number; -type Types = RedisCommandArgument | number; +type HSETObject = Record; -type HSETObject = Record; +type HSETMap = Map; -type HSETMap = Map; +type HSETTuples = Array<[HashTypes, HashTypes]> | Array; -type HSETTuples = Array<[Types, Types]> | Array; +type GenericArguments = [key: RedisArgument]; -type GenericArguments = [key: RedisCommandArgument]; - -type SingleFieldArguments = [...generic: GenericArguments, field: Types, value: Types]; +type SingleFieldArguments = [...generic: GenericArguments, field: HashTypes, value: HashTypes]; type MultipleFieldsArguments = [...generic: GenericArguments, value: HSETObject | HSETMap | HSETTuples]; -export function transformArguments(...[ key, value, fieldValue ]: SingleFieldArguments | MultipleFieldsArguments): RedisCommandArguments { - const args: RedisCommandArguments = ['HSET', key]; +export type HSETArguments = SingleFieldArguments | MultipleFieldsArguments; + +export default { + FIRST_KEY_INDEX: 1, + transformArguments(...[key, value, fieldValue]: SingleFieldArguments | MultipleFieldsArguments) { + const args: Array = ['HSET', key]; if (typeof value === 'string' || typeof value === 'number' || Buffer.isBuffer(value)) { - args.push( - convertValue(value), - convertValue(fieldValue!) - ); + args.push( + convertValue(value), + convertValue(fieldValue!) + ); } else if (value instanceof Map) { - pushMap(args, value); + pushMap(args, value); } else if (Array.isArray(value)) { - pushTuples(args, value); + pushTuples(args, value); } else { - pushObject(args, value); + pushObject(args, value); } return args; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; + +function pushMap(args: Array, map: HSETMap): void { + for (const [key, value] of map.entries()) { + args.push( + convertValue(key), + convertValue(value) + ); + } } -function pushMap(args: RedisCommandArguments, map: HSETMap): void { - for (const [key, value] of map.entries()) { - args.push( - convertValue(key), - convertValue(value) - ); +function pushTuples(args: Array, tuples: HSETTuples): void { + for (const tuple of tuples) { + if (Array.isArray(tuple)) { + pushTuples(args, tuple); + continue; } -} - -function pushTuples(args: RedisCommandArguments, tuples: HSETTuples): void { - for (const tuple of tuples) { - if (Array.isArray(tuple)) { - pushTuples(args, tuple); - continue; - } - args.push(convertValue(tuple)); - } + args.push(convertValue(tuple)); + } } -function pushObject(args: RedisCommandArguments, object: HSETObject): void { - for (const key of Object.keys(object)) { - args.push( - convertValue(key), - convertValue(object[key]) - ); - } +function pushObject(args: Array, object: HSETObject): void { + for (const key of Object.keys(object)) { + args.push( + convertValue(key), + convertValue(object[key]) + ); + } } -function convertValue(value: Types): RedisCommandArgument { - return typeof value === 'number' ? - value.toString() : - value; +function convertValue(value: HashTypes): RedisArgument { + return typeof value === 'number' ? + value.toString() : + value; } - -export declare function transformReply(): number; diff --git a/packages/client/lib/commands/HSETNX.ts b/packages/client/lib/commands/HSETNX.ts index 9ac6ef0edd8..d26c42a76b4 100644 --- a/packages/client/lib/commands/HSETNX.ts +++ b/packages/client/lib/commands/HSETNX.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, Command, NumberReply } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + field: RedisArgument, + value: RedisArgument + ) { return ['HSETNX', key, field, value]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply<0 | 1> +} as const satisfies Command; diff --git a/packages/client/lib/commands/HSTRLEN.ts b/packages/client/lib/commands/HSTRLEN.ts index a820e6c5643..61e2e1b1acd 100644 --- a/packages/client/lib/commands/HSTRLEN.ts +++ b/packages/client/lib/commands/HSTRLEN.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - field: RedisCommandArgument -): RedisCommandArguments { - return ['HSTRLEN', key, field]; -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, field: RedisArgument) { + return ['HSETLEN', key, field]; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/HVALS.ts b/packages/client/lib/commands/HVALS.ts index ef63fdc7f8a..ee4193f5cec 100644 --- a/packages/client/lib/commands/HVALS.ts +++ b/packages/client/lib/commands/HVALS.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['HVALS', key]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INCR.ts b/packages/client/lib/commands/INCR.ts index 2f9a9adfe2b..eb38256c9dc 100644 --- a/packages/client/lib/commands/INCR.ts +++ b/packages/client/lib/commands/INCR.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['INCR', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INCRBY.ts b/packages/client/lib/commands/INCRBY.ts index 75c61156d6b..5e94348fe63 100644 --- a/packages/client/lib/commands/INCRBY.ts +++ b/packages/client/lib/commands/INCRBY.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - increment: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, increment: number) { return ['INCRBY', key, increment.toString()]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INCRBYFLOAT.ts b/packages/client/lib/commands/INCRBYFLOAT.ts index ace3702339e..16f59e68c17 100644 --- a/packages/client/lib/commands/INCRBYFLOAT.ts +++ b/packages/client/lib/commands/INCRBYFLOAT.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - increment: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, increment: number) { return ['INCRBYFLOAT', key, increment.toString()]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/INFO.ts b/packages/client/lib/commands/INFO.ts index 8ab24221b26..3adc3efd334 100644 --- a/packages/client/lib/commands/INFO.ts +++ b/packages/client/lib/commands/INFO.ts @@ -1,13 +1,15 @@ -export const IS_READ_ONLY = true; +import { RedisArgument, VerbatimStringReply, Command } from '../RESP/types'; -export function transformArguments(section?: string): Array { - const args = ['INFO']; +export default { + IS_READ_ONLY: true, + transformArguments(section?: RedisArgument) { + const args: Array = ['INFO']; if (section) { - args.push(section); + args.push(section); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => VerbatimStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/KEYS.ts b/packages/client/lib/commands/KEYS.ts index c96ee001436..507681a56aa 100644 --- a/packages/client/lib/commands/KEYS.ts +++ b/packages/client/lib/commands/KEYS.ts @@ -1,7 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(pattern: RedisCommandArgument): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(pattern: RedisArgument) { return ['KEYS', pattern]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LATENCY_GRAPH.ts b/packages/client/lib/commands/LATENCY_GRAPH.ts index e4e078b90f2..a789a79075a 100644 --- a/packages/client/lib/commands/LATENCY_GRAPH.ts +++ b/packages/client/lib/commands/LATENCY_GRAPH.ts @@ -1,25 +1,25 @@ -import { RedisCommandArguments } from '.'; +// import { RedisCommandArguments } from '.'; -export type EventType = - 'active-defrag-cycle' - | 'aof-fsync-always' - | 'aof-stat' - | 'aof-rewrite-diff-write' - | 'aof-rename' - | 'aof-write' - | 'aof-write-active-child' - | 'aof-write-alone' - | 'aof-write-pending-fsync' - | 'command' - | 'expire-cycle' - | 'eviction-cycle' - | 'eviction-del' - | 'fast-command' - | 'fork' - | 'rdb-unlink-temp-file'; +// export type EventType = +// 'active-defrag-cycle' +// | 'aof-fsync-always' +// | 'aof-stat' +// | 'aof-rewrite-diff-write' +// | 'aof-rename' +// | 'aof-write' +// | 'aof-write-active-child' +// | 'aof-write-alone' +// | 'aof-write-pending-fsync' +// | 'command' +// | 'expire-cycle' +// | 'eviction-cycle' +// | 'eviction-del' +// | 'fast-command' +// | 'fork' +// | 'rdb-unlink-temp-file'; -export function transformArguments(event: EventType): RedisCommandArguments { - return ['LATENCY', 'GRAPH', event]; -} +// export function transformArguments(event: EventType): RedisCommandArguments { +// return ['LATENCY', 'GRAPH', event]; +// } -export declare function transformReply(): string; +// export declare function transformReply(): string; diff --git a/packages/client/lib/commands/LCS.ts b/packages/client/lib/commands/LCS.ts index b075b73e8a8..b798f12aa37 100644 --- a/packages/client/lib/commands/LCS.ts +++ b/packages/client/lib/commands/LCS.ts @@ -1,18 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - return [ - 'LCS', - key1, - key2 - ]; -} - -export declare function transformReply(): string | Buffer; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key1: RedisArgument, + key2: RedisArgument + ) { + return ['LCS', key1, key2]; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_IDX.ts b/packages/client/lib/commands/LCS_IDX.ts index 262a02ba4c6..b9961830578 100644 --- a/packages/client/lib/commands/LCS_IDX.ts +++ b/packages/client/lib/commands/LCS_IDX.ts @@ -1,42 +1,48 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { RangeReply, RawRangeReply, transformRangeReply } from './generic-transformers'; -import { transformArguments as transformLcsArguments } from './LCS'; +// import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NumberReply, Resp2Reply, Command, TuplesReply } from '../RESP/types'; +// import LCS from './LCS'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; +// export interface LcsIdxOptions { +// MINMATCHLEN?: number; +// } -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - const args = transformLcsArguments(key1, key2); - args.push('IDX'); - return args; -} +// export type LcsIdxRange = TuplesReply<[ +// start: NumberReply, +// end: NumberReply +// ]>; -type RawReply = [ - 'matches', - Array<[ - key1: RawRangeReply, - key2: RawRangeReply - ]>, - 'len', - number -]; +// export type LcsIdxMatches = ArrayReply< +// TuplesReply<[ +// key1: LcsIdxRange, +// key2: LcsIdxRange +// ]> +// >; -interface Reply { - matches: Array<{ - key1: RangeReply; - key2: RangeReply; - }>; - length: number; -} +// export type LcsIdxReply = TuplesToMapReply<[ +// [BlobStringReply<'matches'>, LcsIdxMatches], +// [BlobStringReply<'len'>, NumberReply] +// ]>; -export function transformReply(reply: RawReply): Reply { - return { - matches: reply[1].map(([key1, key2]) => ({ - key1: transformRangeReply(key1), - key2: transformRangeReply(key2) - })), - length: reply[3] - }; -} +// export default { +// FIRST_KEY_INDEX: LCS.FIRST_KEY_INDEX, +// IS_READ_ONLY: LCS.IS_READ_ONLY, +// transformArguments( +// key1: RedisArgument, +// key2: RedisArgument, +// options?: LcsIdxOptions +// ) { +// const args = LCS.transformArguments(key1, key2); + +// if (options?.MINMATCHLEN) { +// args.push('MINMATCHLEN', options.MINMATCHLEN.toString()); +// } + +// return args; +// }, +// transformReply: { +// 2: (reply: Resp2Reply) => ({ +// matches: reply[1], +// len: reply[2] +// }), +// 3: undefined as unknown as () => LcsIdxReply +// } +// } as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts index 989870d6ca2..2b6a87672a9 100644 --- a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts +++ b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts @@ -1,45 +1,47 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { RangeReply, RawRangeReply, transformRangeReply } from './generic-transformers'; -import { transformArguments as transformLcsArguments } from './LCS'; +// import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NumberReply, Resp2Reply, Command, TuplesReply } from '../RESP/types'; +// import LCS from './LCS'; +// import { LcsIdxRange } from './LCS_IDX'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; +// interface LcsIdxOptions { +// MINMATCHLEN?: number; +// } -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - const args = transformLcsArguments(key1, key2); - args.push('IDX', 'WITHMATCHLEN'); - return args; -} +// export type LcsIdxWithMatchLenMatches = ArrayReply< +// TuplesReply<[ +// key1: LcsIdxRange, +// key2: LcsIdxRange, +// len: NumberReply +// ]> +// >; -type RawReply = [ - 'matches', - Array<[ - key1: RawRangeReply, - key2: RawRangeReply, - length: number - ]>, - 'len', - number -]; +// export type LcsIdxReply = TuplesToMapReply<[ +// [BlobStringReply<'matches'>, LcsIdxWithMatchLenMatches], +// [BlobStringReply<'len'>, NumberReply] +// ]>; -interface Reply { - matches: Array<{ - key1: RangeReply; - key2: RangeReply; - length: number; - }>; - length: number; -} +// export default { +// FIRST_KEY_INDEX: LCS.FIRST_KEY_INDEX, +// IS_READ_ONLY: LCS.IS_READ_ONLY, +// transformArguments( +// key1: RedisArgument, +// key2: RedisArgument, +// options?: LcsIdxOptions +// ) { +// const args = LCS.transformArguments(key1, key2); -export function transformReply(reply: RawReply): Reply { - return { - matches: reply[1].map(([key1, key2, length]) => ({ - key1: transformRangeReply(key1), - key2: transformRangeReply(key2), - length - })), - length: reply[3] - }; -} +// if (options?.MINMATCHLEN) { +// args.push('MINMATCHLEN', options.MINMATCHLEN.toString()); +// } + +// args.push('WITHMATCHLEN'); + +// return args; +// }, +// transformReply: { +// 2: (reply: Resp2Reply) => ({ +// matches: reply[1], +// len: reply[2] +// }), +// 3: undefined as unknown as () => LcsIdxReply +// } +// } as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_LEN.ts b/packages/client/lib/commands/LCS_LEN.ts index a5121e4c13f..d5d0e77e4d6 100644 --- a/packages/client/lib/commands/LCS_LEN.ts +++ b/packages/client/lib/commands/LCS_LEN.ts @@ -1,15 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformLcsArguments } from './LCS'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import LCS from './LCS'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LCS'; - -export function transformArguments( - key1: RedisCommandArgument, - key2: RedisCommandArgument -): RedisCommandArguments { - const args = transformLcsArguments(key1, key2); +export default { + FIRST_KEY_INDEX: LCS.FIRST_KEY_INDEX, + IS_READ_ONLY: LCS.IS_READ_ONLY, + transformArguments( + key1: RedisArgument, + key2: RedisArgument + ) { + const args = LCS.transformArguments(key1, key2); args.push('LEN'); return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LINDEX.ts b/packages/client/lib/commands/LINDEX.ts index 8e74ad8aae6..0478bf9dc41 100644 --- a/packages/client/lib/commands/LINDEX.ts +++ b/packages/client/lib/commands/LINDEX.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - index: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, index: number) { return ['LINDEX', key, index.toString()]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LINSERT.ts b/packages/client/lib/commands/LINSERT.ts index 0a8e1f32ba4..4bdc77de5a4 100644 --- a/packages/client/lib/commands/LINSERT.ts +++ b/packages/client/lib/commands/LINSERT.ts @@ -1,22 +1,23 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; type LInsertPosition = 'BEFORE' | 'AFTER'; -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, position: LInsertPosition, - pivot: RedisCommandArgument, - element: RedisCommandArgument -): RedisCommandArguments { + pivot: RedisArgument, + element: RedisArgument + ) { return [ - 'LINSERT', - key, - position, - pivot, - element + 'LINSERT', + key, + position, + pivot, + element ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LLEN.ts b/packages/client/lib/commands/LLEN.ts index 3410e57d424..dda59ddf291 100644 --- a/packages/client/lib/commands/LLEN.ts +++ b/packages/client/lib/commands/LLEN.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['LLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LMOVE.ts b/packages/client/lib/commands/LMOVE.ts index 849c6385f5a..3ec118b9905 100644 --- a/packages/client/lib/commands/LMOVE.ts +++ b/packages/client/lib/commands/LMOVE.ts @@ -1,21 +1,21 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; import { ListSide } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + source: RedisArgument, + destination: RedisArgument, sourceSide: ListSide, destinationSide: ListSide -): RedisCommandArguments { + ) { return [ - 'LMOVE', - source, - destination, - sourceSide, - destinationSide, + 'LMOVE', + source, + destination, + sourceSide, + destinationSide, ]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LMPOP.ts b/packages/client/lib/commands/LMPOP.ts index 29d868b982f..5cefc296302 100644 --- a/packages/client/lib/commands/LMPOP.ts +++ b/packages/client/lib/commands/LMPOP.ts @@ -1,22 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformLMPopArguments, LMPopOptions, ListSide } from './generic-transformers'; +import { NullReply, TuplesReply, BlobStringReply, Command } from '../RESP/types'; +import { transformLMPopArguments, LMPopOptions, ListSide, RedisVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; - -export function transformArguments( - keys: RedisCommandArgument | Array, +export default { + FIRST_KEY_INDEX: 2, + transformArguments( + keys: RedisVariadicArgument, side: ListSide, options?: LMPopOptions -): RedisCommandArguments { + ) { return transformLMPopArguments( - ['LMPOP'], - keys, - side, - options + ['LMPOP'], + keys, + side, + options ); -} - -export declare function transformReply(): null | [ - key: string, - elements: Array -]; + }, + transformReply: undefined as unknown as () => NullReply | TuplesReply<[ + key: BlobStringReply, + elements: Array + ]> +} as const satisfies Command; diff --git a/packages/client/lib/commands/LOLWUT.ts b/packages/client/lib/commands/LOLWUT.ts index 5d5fc726065..7a6c8329d69 100644 --- a/packages/client/lib/commands/LOLWUT.ts +++ b/packages/client/lib/commands/LOLWUT.ts @@ -1,19 +1,20 @@ -import { RedisCommandArgument } from '.'; +import { BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(version?: number, ...optionalArguments: Array): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(version?: number, ...optionalArguments: Array) { const args = ['LOLWUT']; if (version) { - args.push( - 'VERSION', - version.toString(), - ...optionalArguments.map(String), - ); + args.push( + 'VERSION', + version.toString(), + ...optionalArguments.map(String), + ); } return args; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPOP.spec.ts b/packages/client/lib/commands/LPOP.spec.ts index d694fb10588..0e432981a2b 100644 --- a/packages/client/lib/commands/LPOP.spec.ts +++ b/packages/client/lib/commands/LPOP.spec.ts @@ -1,26 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPOP'; +import LPOP from './LPOP'; describe('LPOP', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['LPOP', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LPOP.transformArguments('key'), + ['LPOP', 'key'] + ); + }); - testUtils.testWithClient('client.lPop', async client => { - assert.equal( - await client.lPop('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lPop', async cluster => { - assert.equal( - await cluster.lPop('key'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPop', async client => { + assert.equal( + await client.lPop('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPOP.ts b/packages/client/lib/commands/LPOP.ts index 5dd1bea5196..7c85c30f9a1 100644 --- a/packages/client/lib/commands/LPOP.ts +++ b/packages/client/lib/commands/LPOP.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['LPOP', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPOP_COUNT.spec.ts b/packages/client/lib/commands/LPOP_COUNT.spec.ts index 9d87fad3862..691af966c56 100644 --- a/packages/client/lib/commands/LPOP_COUNT.spec.ts +++ b/packages/client/lib/commands/LPOP_COUNT.spec.ts @@ -1,28 +1,24 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPOP_COUNT'; +import LPOP_COUNT from './LPOP_COUNT'; describe('LPOP COUNT', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['LPOP', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LPOP_COUNT.transformArguments('key', 1), + ['LPOP', 'key', '1'] + ); + }); - testUtils.testWithClient('client.lPopCount', async client => { - assert.equal( - await client.lPopCount('key', 1), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lPopCount', async cluster => { - assert.equal( - await cluster.lPopCount('key', 1), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPopCount', async client => { + assert.equal( + await client.lPopCount('key', 1), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPOP_COUNT.ts b/packages/client/lib/commands/LPOP_COUNT.ts index 021517b018a..a1536e78dcb 100644 --- a/packages/client/lib/commands/LPOP_COUNT.ts +++ b/packages/client/lib/commands/LPOP_COUNT.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NullReply, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, count: number) { return ['LPOP', key, count.toString()]; -} - -export declare function transformReply(): Array | null; + }, + transformReply: undefined as unknown as () => NullReply | ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPOS.spec.ts b/packages/client/lib/commands/LPOS.spec.ts index 6b6050f2c3b..21c8a630f54 100644 --- a/packages/client/lib/commands/LPOS.spec.ts +++ b/packages/client/lib/commands/LPOS.spec.ts @@ -1,58 +1,54 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPOS'; +import LPOS from './LPOS'; describe('LPOS', () => { - testUtils.isVersionGreaterThanHook([6, 0, 6]); + testUtils.isVersionGreaterThanHook([6, 0, 6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'element'), - ['LPOS', 'key', 'element'] - ); - }); - - it('with RANK', () => { - assert.deepEqual( - transformArguments('key', 'element', { - RANK: 0 - }), - ['LPOS', 'key', 'element', 'RANK', '0'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + LPOS.transformArguments('key', 'element'), + ['LPOS', 'key', 'element'] + ); + }); - it('with MAXLEN', () => { - assert.deepEqual( - transformArguments('key', 'element', { - MAXLEN: 10 - }), - ['LPOS', 'key', 'element', 'MAXLEN', '10'] - ); - }); + it('with RANK', () => { + assert.deepEqual( + LPOS.transformArguments('key', 'element', { + RANK: 0 + }), + ['LPOS', 'key', 'element', 'RANK', '0'] + ); + }); - it('with RANK, MAXLEN', () => { - assert.deepEqual( - transformArguments('key', 'element', { - RANK: 0, - MAXLEN: 10 - }), - ['LPOS', 'key', 'element', 'RANK', '0', 'MAXLEN', '10'] - ); - }); + it('with MAXLEN', () => { + assert.deepEqual( + LPOS.transformArguments('key', 'element', { + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'MAXLEN', '10'] + ); }); - testUtils.testWithClient('client.lPos', async client => { - assert.equal( - await client.lPos('key', 'element'), - null - ); - }, GLOBAL.SERVERS.OPEN); + it('with RANK, MAXLEN', () => { + assert.deepEqual( + LPOS.transformArguments('key', 'element', { + RANK: 0, + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'RANK', '0', 'MAXLEN', '10'] + ); + }); + }); - testUtils.testWithCluster('cluster.lPos', async cluster => { - assert.equal( - await cluster.lPos('key', 'element'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPos', async client => { + assert.equal( + await client.lPos('key', 'element'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPOS.ts b/packages/client/lib/commands/LPOS.ts index 1f2e34ab88e..047d80d7c29 100644 --- a/packages/client/lib/commands/LPOS.ts +++ b/packages/client/lib/commands/LPOS.ts @@ -1,30 +1,31 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; export interface LPosOptions { - RANK?: number; - MAXLEN?: number; + RANK?: number; + MAXLEN?: number; } -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + element: RedisArgument, options?: LPosOptions -): RedisCommandArguments { + ) { const args = ['LPOS', key, element]; - if (typeof options?.RANK === 'number') { + if (options) { + if (typeof options.RANK === 'number') { args.push('RANK', options.RANK.toString()); - } + } - if (typeof options?.MAXLEN === 'number') { + if (typeof options.MAXLEN === 'number') { args.push('MAXLEN', options.MAXLEN.toString()); + } } return args; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPOS_COUNT.spec.ts b/packages/client/lib/commands/LPOS_COUNT.spec.ts index 4b01f2f59b9..111323c55d8 100644 --- a/packages/client/lib/commands/LPOS_COUNT.spec.ts +++ b/packages/client/lib/commands/LPOS_COUNT.spec.ts @@ -1,58 +1,54 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPOS_COUNT'; +import LPOS_COUNT from './LPOS_COUNT'; describe('LPOS COUNT', () => { - testUtils.isVersionGreaterThanHook([6, 0, 6]); + testUtils.isVersionGreaterThanHook([6, 0, 6]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'element', 0), - ['LPOS', 'key', 'element', 'COUNT', '0'] - ); - }); - - it('with RANK', () => { - assert.deepEqual( - transformArguments('key', 'element', 0, { - RANK: 0 - }), - ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + LPOS_COUNT.transformArguments('key', 'element', 0), + ['LPOS', 'key', 'element', 'COUNT', '0'] + ); + }); - it('with MAXLEN', () => { - assert.deepEqual( - transformArguments('key', 'element', 0, { - MAXLEN: 10 - }), - ['LPOS', 'key', 'element', 'COUNT', '0', 'MAXLEN', '10'] - ); - }); + it('with RANK', () => { + assert.deepEqual( + LPOS_COUNT.transformArguments('key', 'element', 0, { + RANK: 0 + }), + ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0'] + ); + }); - it('with RANK, MAXLEN', () => { - assert.deepEqual( - transformArguments('key', 'element', 0, { - RANK: 0, - MAXLEN: 10 - }), - ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0', 'MAXLEN', '10'] - ); - }); + it('with MAXLEN', () => { + assert.deepEqual( + LPOS_COUNT.transformArguments('key', 'element', 0, { + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'COUNT', '0', 'MAXLEN', '10'] + ); }); - testUtils.testWithClient('client.lPosCount', async client => { - assert.deepEqual( - await client.lPosCount('key', 'element', 0), - [] - ); - }, GLOBAL.SERVERS.OPEN); + it('with RANK, MAXLEN', () => { + assert.deepEqual( + LPOS_COUNT.transformArguments('key', 'element', 0, { + RANK: 0, + MAXLEN: 10 + }), + ['LPOS', 'key', 'element', 'RANK', '0', 'COUNT', '0', 'MAXLEN', '10'] + ); + }); + }); - testUtils.testWithCluster('cluster.lPosCount', async cluster => { - assert.deepEqual( - await cluster.lPosCount('key', 'element', 0), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPosCount', async client => { + assert.deepEqual( + await client.lPosCount('key', 'element', 0), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPOS_COUNT.ts b/packages/client/lib/commands/LPOS_COUNT.ts index 0549df82db5..91bf602f39c 100644 --- a/packages/client/lib/commands/LPOS_COUNT.ts +++ b/packages/client/lib/commands/LPOS_COUNT.ts @@ -1,27 +1,28 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { LPosOptions } from './LPOS'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; +import LPOS, { LPosOptions } from './LPOS'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './LPOS'; - -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: LPOS.FIRST_KEY_INDEX, + IS_READ_ONLY: LPOS.IS_READ_ONLY, + transformArguments( + key: RedisArgument, + element: RedisArgument, count: number, options?: LPosOptions -): RedisCommandArguments { + ) { const args = ['LPOS', key, element]; if (typeof options?.RANK === 'number') { - args.push('RANK', options.RANK.toString()); + args.push('RANK', options.RANK.toString()); } args.push('COUNT', count.toString()); if (typeof options?.MAXLEN === 'number') { - args.push('MAXLEN', options.MAXLEN.toString()); + args.push('MAXLEN', options.MAXLEN.toString()); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPUSH.spec.ts b/packages/client/lib/commands/LPUSH.spec.ts index b5b1f5084eb..7ed3262ed28 100644 --- a/packages/client/lib/commands/LPUSH.spec.ts +++ b/packages/client/lib/commands/LPUSH.spec.ts @@ -1,35 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPUSH'; +import LPUSH from './LPUSH'; describe('LPUSH', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'field'), - ['LPUSH', 'key', 'field'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['LPUSH', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + LPUSH.transformArguments('key', 'field'), + ['LPUSH', 'key', 'field'] + ); }); - testUtils.testWithClient('client.lPush', async client => { - assert.equal( - await client.lPush('key', 'field'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + it('array', () => { + assert.deepEqual( + LPUSH.transformArguments('key', ['1', '2']), + ['LPUSH', 'key', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.lPush', async cluster => { - assert.equal( - await cluster.lPush('key', 'field'), - 1 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPush', async client => { + assert.equal( + await client.lPush('key', 'field'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPUSH.ts b/packages/client/lib/commands/LPUSH.ts index 7144b146e27..714db2ae9a9 100644 --- a/packages/client/lib/commands/LPUSH.ts +++ b/packages/client/lib/commands/LPUSH.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - elements: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['LPUSH', key], elements);} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, elements: RedisVariadicArgument) { + return pushVariadicArguments(['LPUSH', key], elements); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LPUSHX.spec.ts b/packages/client/lib/commands/LPUSHX.spec.ts index d978e5a588f..8cbe89e31b0 100644 --- a/packages/client/lib/commands/LPUSHX.spec.ts +++ b/packages/client/lib/commands/LPUSHX.spec.ts @@ -1,35 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LPUSHX'; +import LPUSHX from './LPUSHX'; describe('LPUSHX', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'element'), - ['LPUSHX', 'key', 'element'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['LPUSHX', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + LPUSHX.transformArguments('key', 'element'), + ['LPUSHX', 'key', 'element'] + ); }); - testUtils.testWithClient('client.lPushX', async client => { - assert.equal( - await client.lPushX('key', 'element'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('array', () => { + assert.deepEqual( + LPUSHX.transformArguments('key', ['1', '2']), + ['LPUSHX', 'key', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.lPushX', async cluster => { - assert.equal( - await cluster.lPushX('key', 'element'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lPushX', async client => { + assert.equal( + await client.lPushX('key', 'element'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LPUSHX.ts b/packages/client/lib/commands/LPUSHX.ts index 0b518add6da..d6dceda6d02 100644 --- a/packages/client/lib/commands/LPUSHX.ts +++ b/packages/client/lib/commands/LPUSHX.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['LPUSHX', key], element); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, elements: RedisVariadicArgument) { + return pushVariadicArguments(['LPUSHX', key], elements); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LRANGE.spec.ts b/packages/client/lib/commands/LRANGE.spec.ts index dffe6087b80..12f0c8f7b9d 100644 --- a/packages/client/lib/commands/LRANGE.spec.ts +++ b/packages/client/lib/commands/LRANGE.spec.ts @@ -1,27 +1,23 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LRANGE'; +import LRANGE from './LRANGE'; describe('LRANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, -1), - ['LRANGE', 'key', '0', '-1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LRANGE.transformArguments('key', 0, -1), + ['LRANGE', 'key', '0', '-1'] + ); + }); - testUtils.testWithClient('client.lRange', async client => { - assert.deepEqual( - await client.lRange('key', 0, -1), - [] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lRange', async cluster => { - assert.deepEqual( - await cluster.lRange('key', 0, -1), - [] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lRange', async client => { + assert.deepEqual( + await client.lRange('key', 0, -1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LRANGE.ts b/packages/client/lib/commands/LRANGE.ts index df12c57d804..4b4b3488baa 100644 --- a/packages/client/lib/commands/LRANGE.ts +++ b/packages/client/lib/commands/LRANGE.ts @@ -1,20 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, start: number, stop: number -): RedisCommandArguments { +) { return [ - 'LRANGE', - key, - start.toString(), - stop.toString() + 'LRANGE', + key, + start.toString(), + stop.toString() ]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LREM.spec.ts b/packages/client/lib/commands/LREM.spec.ts index 3405f4beb07..bbc3f6096e9 100644 --- a/packages/client/lib/commands/LREM.spec.ts +++ b/packages/client/lib/commands/LREM.spec.ts @@ -1,27 +1,23 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LREM'; +import LREM from './LREM'; describe('LREM', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 'element'), - ['LREM', 'key', '0', 'element'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LREM.transformArguments('key', 0, 'element'), + ['LREM', 'key', '0', 'element'] + ); + }); - testUtils.testWithClient('client.lRem', async client => { - assert.equal( - await client.lRem('key', 0, 'element'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lRem', async cluster => { - assert.equal( - await cluster.lRem('key', 0, 'element'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lRem', async client => { + assert.equal( + await client.lRem('key', 0, 'element'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LREM.ts b/packages/client/lib/commands/LREM.ts index b4951334888..dbd2002be8b 100644 --- a/packages/client/lib/commands/LREM.ts +++ b/packages/client/lib/commands/LREM.ts @@ -1,18 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, count: number, - element: RedisCommandArgument -): RedisCommandArguments { + element: RedisArgument + ) { return [ - 'LREM', - key, - count.toString(), - element + 'LREM', + key, + count.toString(), + element ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LSET.spec.ts b/packages/client/lib/commands/LSET.spec.ts index d7241032cc6..0d34331603b 100644 --- a/packages/client/lib/commands/LSET.spec.ts +++ b/packages/client/lib/commands/LSET.spec.ts @@ -1,28 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LSET'; +import LSET from './LSET'; describe('LSET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 'element'), - ['LSET', 'key', '0', 'element'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LSET.transformArguments('key', 0, 'element'), + ['LSET', 'key', '0', 'element'] + ); + }); - testUtils.testWithClient('client.lSet', async client => { - await client.lPush('key', 'element'); - assert.equal( - await client.lSet('key', 0, 'element'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lSet', async cluster => { - await cluster.lPush('key', 'element'); - assert.equal( - await cluster.lSet('key', 0, 'element'), - 'OK' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lSet', async client => { + assert.equal( + await client.lSet('key', 0, 'element'), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LSET.ts b/packages/client/lib/commands/LSET.ts index 33c7b4cc060..3c5b7867469 100644 --- a/packages/client/lib/commands/LSET.ts +++ b/packages/client/lib/commands/LSET.ts @@ -1,18 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, index: number, - element: RedisCommandArgument -): RedisCommandArguments { + element: RedisArgument + ) { return [ - 'LSET', - key, - index.toString(), - element + 'LREM', + key, + index.toString(), + element ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/LTRIM.spec.ts b/packages/client/lib/commands/LTRIM.spec.ts index 5b6ac5d3660..df151d2ddb5 100644 --- a/packages/client/lib/commands/LTRIM.spec.ts +++ b/packages/client/lib/commands/LTRIM.spec.ts @@ -1,26 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './LTRIM'; +import LTRIM from './LTRIM'; describe('LTRIM', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, -1), - ['LTRIM', 'key', '0', '-1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + LTRIM.transformArguments('key', 0, -1), + ['LTRIM', 'key', '0', '-1'] + ); + }); - testUtils.testWithClient('client.lTrim', async client => { - assert.equal( - await client.lTrim('key', 0, -1), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.lTrim', async cluster => { - assert.equal( - await cluster.lTrim('key', 0, -1), - 'OK' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('lTrim', async client => { + assert.equal( + await client.lTrim('key', 0, -1), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/LTRIM.ts b/packages/client/lib/commands/LTRIM.ts index 668497cdde6..79377ed1d5b 100644 --- a/packages/client/lib/commands/LTRIM.ts +++ b/packages/client/lib/commands/LTRIM.ts @@ -1,18 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, start: number, stop: number -): RedisCommandArguments { + ) { return [ - 'LTRIM', - key, - start.toString(), - stop.toString() + 'LREM', + key, + start.toString(), + stop.toString() ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MGET.spec.ts b/packages/client/lib/commands/MGET.spec.ts index 9ff47895f4e..b9fa49bbdd6 100644 --- a/packages/client/lib/commands/MGET.spec.ts +++ b/packages/client/lib/commands/MGET.spec.ts @@ -1,26 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MGET'; +import MGET from './MGET'; describe('MGET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['MGET', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MGET.transformArguments(['1', '2']), + ['MGET', '1', '2'] + ); + }); - testUtils.testWithClient('client.mGet', async client => { - assert.deepEqual( - await client.mGet(['key']), - [null] - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.mGet', async cluster => { - assert.deepEqual( - await cluster.mGet(['key']), - [null] - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('mGet', async client => { + assert.deepEqual( + await client.mGet(['key']), + [null] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/MGET.ts b/packages/client/lib/commands/MGET.ts index 6635a2ca20c..0f0f9e52ffb 100644 --- a/packages/client/lib/commands/MGET.ts +++ b/packages/client/lib/commands/MGET.ts @@ -1,13 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(keys: Array) { return ['MGET', ...keys]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => Array +} as const satisfies Command; diff --git a/packages/client/lib/commands/MIGRATE.ts b/packages/client/lib/commands/MIGRATE.ts index aaff3164081..adb1ae8d197 100644 --- a/packages/client/lib/commands/MIGRATE.ts +++ b/packages/client/lib/commands/MIGRATE.ts @@ -1,65 +1,66 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; import { AuthOptions } from './AUTH'; interface MigrateOptions { - COPY?: true; - REPLACE?: true; - AUTH?: AuthOptions; + COPY?: true; + REPLACE?: true; + AUTH?: AuthOptions; } -export function transformArguments( - host: RedisCommandArgument, +export default { + transformArguments( + host: RedisArgument, port: number, - key: RedisCommandArgument | Array, + key: RedisArgument | Array, destinationDb: number, timeout: number, options?: MigrateOptions -): RedisCommandArguments { + ) { const args = ['MIGRATE', host, port.toString()], - isKeyArray = Array.isArray(key); - + isKeyArray = Array.isArray(key); + if (isKeyArray) { - args.push(''); + args.push(''); } else { - args.push(key); + args.push(key); } - + args.push( - destinationDb.toString(), - timeout.toString() + destinationDb.toString(), + timeout.toString() ); - + if (options?.COPY) { - args.push('COPY'); + args.push('COPY'); } - + if (options?.REPLACE) { - args.push('REPLACE'); + args.push('REPLACE'); } - + if (options?.AUTH) { - if (options.AUTH.username) { - args.push( - 'AUTH2', - options.AUTH.username, - options.AUTH.password - ); - } else { - args.push( - 'AUTH', - options.AUTH.password - ); - } - } - - if (isKeyArray) { + if (options.AUTH.username) { args.push( - 'KEYS', - ...key + 'AUTH2', + options.AUTH.username, + options.AUTH.password ); + } else { + args.push( + 'AUTH', + options.AUTH.password + ); + } } - + + if (isKeyArray) { + args.push( + 'KEYS', + ...key + ); + } + return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_LIST.ts b/packages/client/lib/commands/MODULE_LIST.ts index d75b2428308..5ab297faecc 100644 --- a/packages/client/lib/commands/MODULE_LIST.ts +++ b/packages/client/lib/commands/MODULE_LIST.ts @@ -1,5 +1,23 @@ -export function transformArguments(): Array { - return ['MODULE', 'LIST']; -} +import { ArrayReply, BlobStringReply, NumberReply, Command, Resp2Reply, TuplesToMapReply } from '../RESP/types'; + +export type ModuleListReply = ArrayReply, BlobStringReply], + [BlobStringReply<'version'>, NumberReply], +]>>; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['MODULE', 'LIST']; + }, + transformReply: { + 2: (reply: Resp2Reply) => { + return reply.map(module => ({ + name: module[1], + version: module[3] + })); + }, + 3: undefined as unknown as () => ModuleListReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_LOAD.ts b/packages/client/lib/commands/MODULE_LOAD.ts index b44b4b57ce6..3fab206eeff 100644 --- a/packages/client/lib/commands/MODULE_LOAD.ts +++ b/packages/client/lib/commands/MODULE_LOAD.ts @@ -1,11 +1,17 @@ -export function transformArguments(path: string, moduleArgs?: Array): Array { +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(path: RedisArgument, moduleArguments?: Array) { const args = ['MODULE', 'LOAD', path]; - if (moduleArgs) { - args.push(...moduleArgs); + if (moduleArguments) { + return args.concat(moduleArguments); } - + return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MODULE_UNLOAD.ts b/packages/client/lib/commands/MODULE_UNLOAD.ts index d5927778fe6..8d1e64800a9 100644 --- a/packages/client/lib/commands/MODULE_UNLOAD.ts +++ b/packages/client/lib/commands/MODULE_UNLOAD.ts @@ -1,5 +1,10 @@ -export function transformArguments(name: string): Array { - return ['MODULE', 'UNLOAD', name]; -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(name: RedisArgument) { + return ['MODULE', 'UNLOAD', name]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MOVE.ts b/packages/client/lib/commands/MOVE.ts index 17cc6742c5f..60aac4b1457 100644 --- a/packages/client/lib/commands/MOVE.ts +++ b/packages/client/lib/commands/MOVE.ts @@ -1,7 +1,9 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export function transformArguments(key: string, db: number): Array { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, db: number) { return ['MOVE', key, db.toString()]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/MSET.ts b/packages/client/lib/commands/MSET.ts index bd7111659d1..136fde3e7f7 100644 --- a/packages/client/lib/commands/MSET.ts +++ b/packages/client/lib/commands/MSET.ts @@ -1,24 +1,27 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; export type MSetArguments = - Array<[RedisCommandArgument, RedisCommandArgument]> | - Array | - Record; + Array<[RedisArgument, RedisArgument]> | + Array | + Record; -export function transformArguments(toSet: MSetArguments): RedisCommandArguments { - const args: RedisCommandArguments = ['MSET']; +export function mSetArguments(command: string, toSet: MSetArguments) { + const args: Array = [command]; - if (Array.isArray(toSet)) { - args.push(...toSet.flat()); - } else { - for (const key of Object.keys(toSet)) { - args.push(key, toSet[key]); - } + if (Array.isArray(toSet)) { + args.push(...toSet.flat()); + } else { + for (const tuple of Object.entries(toSet)) { + args.push(...tuple); } + } - return args; + return args; } -export declare function transformReply(): RedisCommandArgument; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: mSetArguments.bind(undefined, 'MSET'), + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/MSETNX.ts b/packages/client/lib/commands/MSETNX.ts index 0ef33936114..4867b3ea092 100644 --- a/packages/client/lib/commands/MSETNX.ts +++ b/packages/client/lib/commands/MSETNX.ts @@ -1,20 +1,9 @@ -import { RedisCommandArguments } from '.'; -import { MSetArguments } from './MSET'; - -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(toSet: MSetArguments): RedisCommandArguments { - const args: RedisCommandArguments = ['MSETNX']; - - if (Array.isArray(toSet)) { - args.push(...toSet.flat()); - } else { - for (const key of Object.keys(toSet)) { - args.push(key, toSet[key]); - } - } - - return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { mSetArguments } from './MSET'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: mSetArguments.bind(undefined, 'MSETNX'), + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_ENCODING.ts b/packages/client/lib/commands/OBJECT_ENCODING.ts index ac219ae89ed..e74c3f99ebd 100644 --- a/packages/client/lib/commands/OBJECT_ENCODING.ts +++ b/packages/client/lib/commands/OBJECT_ENCODING.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'ENCODING', key]; -} - -export declare function transformReply(): string | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_FREQ.ts b/packages/client/lib/commands/OBJECT_FREQ.ts index 071d16f2748..b6757c6c57b 100644 --- a/packages/client/lib/commands/OBJECT_FREQ.ts +++ b/packages/client/lib/commands/OBJECT_FREQ.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'FREQ', key]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_IDLETIME.ts b/packages/client/lib/commands/OBJECT_IDLETIME.ts index 38847d6f4cf..f0fef24e2d8 100644 --- a/packages/client/lib/commands/OBJECT_IDLETIME.ts +++ b/packages/client/lib/commands/OBJECT_IDLETIME.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'IDLETIME', key]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/OBJECT_REFCOUNT.ts b/packages/client/lib/commands/OBJECT_REFCOUNT.ts index 9fd259b5b90..c8381a76bf7 100644 --- a/packages/client/lib/commands/OBJECT_REFCOUNT.ts +++ b/packages/client/lib/commands/OBJECT_REFCOUNT.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['OBJECT', 'REFCOUNT', key]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PERSIST.ts b/packages/client/lib/commands/PERSIST.ts index d7c9f8623e4..64637fabc14 100644 --- a/packages/client/lib/commands/PERSIST.ts +++ b/packages/client/lib/commands/PERSIST.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['PERSIST', key]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIRE.ts b/packages/client/lib/commands/PEXPIRE.ts index cbb5666a514..4d64281e922 100644 --- a/packages/client/lib/commands/PEXPIRE.ts +++ b/packages/client/lib/commands/PEXPIRE.ts @@ -1,19 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - milliseconds: number, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + ms: number, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { - const args = ['PEXPIRE', key, milliseconds.toString()]; + ) { + const args = ['PEXPIRE', key, ms.toString()]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIREAT.ts b/packages/client/lib/commands/PEXPIREAT.ts index da912ec4fcb..cbae589fba6 100644 --- a/packages/client/lib/commands/PEXPIREAT.ts +++ b/packages/client/lib/commands/PEXPIREAT.ts @@ -1,24 +1,21 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { transformPXAT } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - millisecondsTimestamp: number | Date, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + msTimestamp: number | Date, mode?: 'NX' | 'XX' | 'GT' | 'LT' -): RedisCommandArguments { - const args = [ - 'PEXPIREAT', - key, - transformPXAT(millisecondsTimestamp) - ]; + ) { + const args = ['PEXPIREAT', key, transformPXAT(msTimestamp)]; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PEXPIRETIME.ts b/packages/client/lib/commands/PEXPIRETIME.ts index 4c1acba8f04..49877701b60 100644 --- a/packages/client/lib/commands/PEXPIRETIME.ts +++ b/packages/client/lib/commands/PEXPIRETIME.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return ['PEXPIRETIME', key]; -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { + return ['PEXPIREAT', key]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PFADD.ts b/packages/client/lib/commands/PFADD.ts index 8c8985de890..41bf2adfe5e 100644 --- a/packages/client/lib/commands/PFADD.ts +++ b/packages/client/lib/commands/PFADD.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, element?: RedisVariadicArgument) { + const args = ['PFADD', key]; + if (!element) return args; -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['PFADD', key], element); -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + return pushVariadicArguments(args, element); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PFCOUNT.ts b/packages/client/lib/commands/PFCOUNT.ts index a4cf2dbcb26..5b46eb00d92 100644 --- a/packages/client/lib/commands/PFCOUNT.ts +++ b/packages/client/lib/commands/PFCOUNT.ts @@ -1,12 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['PFCOUNT'], key); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisVariadicArgument) { + return pushVariadicArguments(['PFCOUNT'], key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PFMERGE.ts b/packages/client/lib/commands/PFMERGE.ts index e934062b3fe..eeeeb5173db 100644 --- a/packages/client/lib/commands/PFMERGE.ts +++ b/packages/client/lib/commands/PFMERGE.ts @@ -1,10 +1,16 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + destination: RedisArgument, + source?: RedisVariadicArgument + ) { + const args = ['PFMERGE', destination]; + if (!source) return args; -export function transformArguments(destination: string, source: string | Array): RedisCommandArguments { - return pushVerdictArguments(['PFMERGE', destination], source); -} - -export declare function transformReply(): string; + return pushVariadicArguments(args, source); + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PING.spec.ts b/packages/client/lib/commands/PING.spec.ts index 06cbae43a13..5ea68074d72 100644 --- a/packages/client/lib/commands/PING.spec.ts +++ b/packages/client/lib/commands/PING.spec.ts @@ -1,37 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './PING'; +import PING from './PING'; describe('PING', () => { - describe('transformArguments', () => { - it('default', () => { - assert.deepEqual( - transformArguments(), - ['PING'] - ); - }); - - it('with message', () => { - assert.deepEqual( - transformArguments('message'), - ['PING', 'message'] - ); - }); + describe('transformArguments', () => { + it('default', () => { + assert.deepEqual( + PING.transformArguments(), + ['PING'] + ); }); - describe('client.ping', () => { - testUtils.testWithClient('string', async client => { - assert.equal( - await client.ping(), - 'PONG' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('buffer', async client => { - assert.deepEqual( - await client.ping(client.commandOptions({ returnBuffers: true })), - Buffer.from('PONG') - ); - }, GLOBAL.SERVERS.OPEN); + it('with message', () => { + assert.deepEqual( + PING.transformArguments('message'), + ['PING', 'message'] + ); }); + }); + + testUtils.testAll('ping', async client => { + assert.equal( + await client.ping(), + 'PONG' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/PING.ts b/packages/client/lib/commands/PING.ts index 95fa006122d..5ca95f932d4 100644 --- a/packages/client/lib/commands/PING.ts +++ b/packages/client/lib/commands/PING.ts @@ -1,12 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(message?: RedisCommandArgument): RedisCommandArguments { - const args: RedisCommandArguments = ['PING']; +export default { + IS_READ_ONLY: true, + transformArguments(message?: RedisArgument) { + const args: Array = ['PING']; if (message) { - args.push(message); + args.push(message); } return args; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PSETEX.ts b/packages/client/lib/commands/PSETEX.ts index f2739b6e274..4e345a1a1c9 100644 --- a/packages/client/lib/commands/PSETEX.ts +++ b/packages/client/lib/commands/PSETEX.ts @@ -1,18 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - milliseconds: number, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + ms: number, + value: RedisArgument + ) { return [ - 'PSETEX', - key, - milliseconds.toString(), - value + 'PSETEX', + key, + ms.toString(), + value ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/PTTL.ts b/packages/client/lib/commands/PTTL.ts index a2975623f7a..35854337877 100644 --- a/packages/client/lib/commands/PTTL.ts +++ b/packages/client/lib/commands/PTTL.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['PTTL', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBLISH.ts b/packages/client/lib/commands/PUBLISH.ts index 7862a0936cb..1566651d9f1 100644 --- a/packages/client/lib/commands/PUBLISH.ts +++ b/packages/client/lib/commands/PUBLISH.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments( - channel: RedisCommandArgument, - message: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(channel: RedisArgument, message: RedisArgument) { return ['PUBLISH', channel, message]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_CHANNELS.ts b/packages/client/lib/commands/PUBSUB_CHANNELS.ts index 86a144ede8e..4bf7abd75dc 100644 --- a/packages/client/lib/commands/PUBSUB_CHANNELS.ts +++ b/packages/client/lib/commands/PUBSUB_CHANNELS.ts @@ -1,13 +1,17 @@ -export const IS_READ_ONLY = true; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export function transformArguments(pattern?: string): Array { - const args = ['PUBSUB', 'CHANNELS']; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(pattern?: RedisArgument) { + const args: Array = ['PUBSUB', 'CHANNELS']; if (pattern) { - args.push(pattern); + args.push(pattern); } return args; -} + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; -export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/PUBSUB_NUMPAT.ts b/packages/client/lib/commands/PUBSUB_NUMPAT.ts index 15be6aa1b18..e8a0738dc72 100644 --- a/packages/client/lib/commands/PUBSUB_NUMPAT.ts +++ b/packages/client/lib/commands/PUBSUB_NUMPAT.ts @@ -1,7 +1,10 @@ -export const IS_READ_ONLY = true; +import { NumberReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['PUBSUB', 'NUMPAT']; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_NUMSUB.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.ts index f47238f8882..aaa6cf749c0 100644 --- a/packages/client/lib/commands/PUBSUB_NUMSUB.ts +++ b/packages/client/lib/commands/PUBSUB_NUMSUB.ts @@ -1,24 +1,23 @@ -import { pushVerdictArguments } from './generic-transformers'; -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments( - channels?: Array | RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(channels?: RedisVariadicArgument) { const args = ['PUBSUB', 'NUMSUB']; - if (channels) return pushVerdictArguments(args, channels); + if (channels) return pushVariadicArguments(args, channels); return args; -} - -export function transformReply(rawReply: Array): Record { - const transformedReply = Object.create(null); - - for (let i = 0; i < rawReply.length; i +=2) { - transformedReply[rawReply[i]] = rawReply[i + 1]; + }, + transformReply(rawReply: ArrayReply) { + const reply = Object.create(null); + let i = 0; + while (i < rawReply.length) { + reply[rawReply[i++].toString()] = rawReply[i++].toString(); } - return transformedReply; -} + return reply as Record; + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts index e998677848a..1403d67fa22 100644 --- a/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts +++ b/packages/client/lib/commands/PUBSUB_SHARDCHANNELS.ts @@ -1,13 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(pattern?: RedisArgument) { + const args: Array = ['PUBSUB', 'bb']; -export function transformArguments( - pattern?: RedisCommandArgument -): RedisCommandArguments { - const args: RedisCommandArguments = ['PUBSUB', 'SHARDCHANNELS']; - if (pattern) args.push(pattern); - return args; -} + if (pattern) { + args.push(pattern); + } -export declare function transformReply(): Array; + return args; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RANDOMKEY.spec.ts b/packages/client/lib/commands/RANDOMKEY.spec.ts index 81c42b2fd83..8cd74669c55 100644 --- a/packages/client/lib/commands/RANDOMKEY.spec.ts +++ b/packages/client/lib/commands/RANDOMKEY.spec.ts @@ -1,19 +1,19 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RANDOMKEY'; +import RANDOMKEY from './RANDOMKEY'; describe('RANDOMKEY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['RANDOMKEY'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RANDOMKEY.transformArguments(), + ['RANDOMKEY'] + ); + }); - testUtils.testWithClient('client.randomKey', async client => { - assert.equal( - await client.randomKey(), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.randomKey', async client => { + assert.equal( + await client.randomKey(), + null + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/RANDOMKEY.ts b/packages/client/lib/commands/RANDOMKEY.ts index f2d511d4dec..81d956209ea 100644 --- a/packages/client/lib/commands/RANDOMKEY.ts +++ b/packages/client/lib/commands/RANDOMKEY.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { NumberReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export function transformArguments(): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments() { return ['RANDOMKEY']; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/READONLY.ts b/packages/client/lib/commands/READONLY.ts index db7db881628..bb15834550e 100644 --- a/packages/client/lib/commands/READONLY.ts +++ b/packages/client/lib/commands/READONLY.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['READONLY']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['READONLY']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/READWRITE.ts b/packages/client/lib/commands/READWRITE.ts index 60dc865e89e..fe70e15d4c8 100644 --- a/packages/client/lib/commands/READWRITE.ts +++ b/packages/client/lib/commands/READWRITE.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['READWRITE']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['READWRITE']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RENAME.spec.ts b/packages/client/lib/commands/RENAME.spec.ts index 49e0af600f6..d0d16115ea3 100644 --- a/packages/client/lib/commands/RENAME.spec.ts +++ b/packages/client/lib/commands/RENAME.spec.ts @@ -1,21 +1,24 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RENAME'; +import RENAME from './RENAME'; describe('RENAME', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('from', 'to'), - ['RENAME', 'from', 'to'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RENAME.transformArguments('source', 'destination'), + ['RENAME', 'source', 'destination'] + ); + }); - testUtils.testWithClient('client.rename', async client => { - await client.set('from', 'value'); - - assert.equal( - await client.rename('from', 'to'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('rename', async client => { + const [, reply] = await Promise.all([ + client.set('{tag}source', 'value'), + client.rename('{tag}source', '{tag}destination') + ]); + + assert.equal(reply, 'OK'); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RENAME.ts b/packages/client/lib/commands/RENAME.ts index 2d1134084fb..ded9e7e0b22 100644 --- a/packages/client/lib/commands/RENAME.ts +++ b/packages/client/lib/commands/RENAME.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - newKey: RedisCommandArgument -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, newKey: RedisArgument) { return ['RENAME', key, newKey]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RENAMENX.spec.ts b/packages/client/lib/commands/RENAMENX.spec.ts index 6345eb5bd09..da1e0c4b480 100644 --- a/packages/client/lib/commands/RENAMENX.spec.ts +++ b/packages/client/lib/commands/RENAMENX.spec.ts @@ -1,21 +1,24 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RENAMENX'; +import RENAMENX from './RENAMENX'; describe('RENAMENX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('from', 'to'), - ['RENAMENX', 'from', 'to'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RENAMENX.transformArguments('source', 'destination'), + ['RENAMENX', 'source', 'destination'] + ); + }); - testUtils.testWithClient('client.renameNX', async client => { - await client.set('from', 'value'); + testUtils.testAll('renameNX', async client => { + const [, reply] = await Promise.all([ + client.set('{tag}source', 'value'), + client.renameNX('{tag}source', '{tag}destination') + ]); - assert.equal( - await client.renameNX('from', 'to'), - true - ); - }, GLOBAL.SERVERS.OPEN); + assert.equal(reply, 1); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RENAMENX.ts b/packages/client/lib/commands/RENAMENX.ts index 322ff0a88cc..01dedc87798 100644 --- a/packages/client/lib/commands/RENAMENX.ts +++ b/packages/client/lib/commands/RENAMENX.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command, NumberReply } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - newKey: RedisCommandArgument -): RedisCommandArguments { +export default { + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, newKey: RedisArgument) { return ['RENAMENX', key, newKey]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/REPLICAOF.ts b/packages/client/lib/commands/REPLICAOF.ts index bd452e0f371..4e2f69f7265 100644 --- a/packages/client/lib/commands/REPLICAOF.ts +++ b/packages/client/lib/commands/REPLICAOF.ts @@ -1,5 +1,10 @@ -export function transformArguments(host: string, port: number): Array { - return ['REPLICAOF', host, port.toString()]; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(host: string, port: number) { + return ['REPLICAOF', host, port.toString()]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RESTORE-ASKING.ts b/packages/client/lib/commands/RESTORE-ASKING.ts index d53d8541cd7..14f6dcbeab3 100644 --- a/packages/client/lib/commands/RESTORE-ASKING.ts +++ b/packages/client/lib/commands/RESTORE-ASKING.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['RESTORE-ASKING']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['RESTORE-ASKING']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ROLE.ts b/packages/client/lib/commands/ROLE.ts index b1d6041fdfa..2cab0a783ed 100644 --- a/packages/client/lib/commands/ROLE.ts +++ b/packages/client/lib/commands/ROLE.ts @@ -1,75 +1,67 @@ -export const IS_READ_ONLY = true; +import { ArrayReply, BlobStringReply, Command, NumberReply } from '../RESP/types'; -export function transformArguments(): Array { - return ['ROLE']; -} - -interface RoleReplyInterface { - role: T; -} - -type RoleMasterRawReply = ['master', number, Array<[string, string, string]>]; - -interface RoleMasterReply extends RoleReplyInterface<'master'> { - replicationOffest: number; - replicas: Array<{ - ip: string; - port: number; - replicationOffest: number; - }>; -} - -type RoleReplicaState = 'connect' | 'connecting' | 'sync' | 'connected'; +type MasterRole = [ + role: BlobStringReply<'master'>, + replicationOffest: NumberReply, + replicas: ArrayReply<[host: BlobStringReply, port: NumberReply, replicationOffest: NumberReply]>, +]; -type RoleReplicaRawReply = ['slave', string, number, RoleReplicaState, number]; +type SlaveRole = [ + role: BlobStringReply<'slave'>, + masterHost: BlobStringReply, + masterPort: NumberReply, + state: BlobStringReply<'connect' | 'connecting' | 'sync' | 'connected'>, + dataReceived: NumberReply +]; -interface RoleReplicaReply extends RoleReplyInterface<'slave'> { - master: { - ip: string; - port: number; - }; - state: RoleReplicaState; - dataReceived: number; -} +type SentinelRole = [ + role: BlobStringReply<'sentinel'>, + masterNames: ArrayReply +]; -type RoleSentinelRawReply = ['sentinel', Array]; +type Role = MasterRole | SlaveRole | SentinelRole; -interface RoleSentinelReply extends RoleReplyInterface<'sentinel'> { - masterNames: Array; -} - -type RoleRawReply = RoleMasterRawReply | RoleReplicaRawReply | RoleSentinelRawReply; - -type RoleReply = RoleMasterReply | RoleReplicaReply | RoleSentinelReply; - -export function transformReply(reply: RoleRawReply): RoleReply { - switch (reply[0]) { - case 'master': - return { - role: 'master', - replicationOffest: reply[1], - replicas: reply[2].map(([ip, port, replicationOffest]) => ({ - ip, - port: Number(port), - replicationOffest: Number(replicationOffest) - })) - }; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['ROLE']; + }, + transformReply(reply: Role) { + switch (reply[0] as Role[0]['DEFAULT']) { + case 'master': { + const [role, replicationOffest, replicas] = reply as MasterRole; + return { + role, + replicationOffest, + replicas: replicas.map(([host, port, replicationOffest]) => ({ + host, + port, + replicationOffest, + })), + }; + } - case 'slave': - return { - role: 'slave', - master: { - ip: reply[1], - port: reply[2] - }, - state: reply[3], - dataReceived: reply[4] - }; + case 'slave': { + const [role, masterHost, masterPort, state, dataReceived] = reply as SlaveRole; + return { + role, + master: { + host: masterHost, + port: masterPort, + }, + state, + dataReceived, + }; + } - case 'sentinel': - return { - role: 'sentinel', - masterNames: reply[1] - }; + case 'sentinel': { + const [role, masterNames] = reply as SentinelRole; + return { + role, + masterNames, + }; + } } -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPOP.spec.ts b/packages/client/lib/commands/RPOP.spec.ts index 6e57afa3216..d22dadccd3d 100644 --- a/packages/client/lib/commands/RPOP.spec.ts +++ b/packages/client/lib/commands/RPOP.spec.ts @@ -1,26 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RPOP'; +import RPOP from './RPOP'; describe('RPOP', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['RPOP', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RPOP.transformArguments('key'), + ['RPOP', 'key'] + ); + }); - testUtils.testWithClient('client.rPop', async client => { - assert.equal( - await client.rPop('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.rPop', async cluster => { - assert.equal( - await cluster.rPop('key'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('rPop', async client => { + assert.equal( + await client.rPop('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RPOP.ts b/packages/client/lib/commands/RPOP.ts index ed696b6d522..f7d0b33d3af 100644 --- a/packages/client/lib/commands/RPOP.ts +++ b/packages/client/lib/commands/RPOP.ts @@ -1,9 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument) { return ['RPOP', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPOPLPUSH.spec.ts b/packages/client/lib/commands/RPOPLPUSH.spec.ts index cef3049bd91..bb045168bf8 100644 --- a/packages/client/lib/commands/RPOPLPUSH.spec.ts +++ b/packages/client/lib/commands/RPOPLPUSH.spec.ts @@ -1,26 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RPOPLPUSH'; +import RPOPLPUSH from './RPOPLPUSH'; describe('RPOPLPUSH', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('source', 'destination'), - ['RPOPLPUSH', 'source', 'destination'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RPOPLPUSH.transformArguments('source', 'destination'), + ['RPOPLPUSH', 'source', 'destination'] + ); + }); - testUtils.testWithClient('client.rPopLPush', async client => { - assert.equal( - await client.rPopLPush('source', 'destination'), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.rPopLPush', async cluster => { - assert.equal( - await cluster.rPopLPush('{tag}source', '{tag}destination'), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('rPopLPush', async client => { + assert.equal( + await client.rPopLPush('{tag}source', '{tag}destination'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RPOPLPUSH.ts b/packages/client/lib/commands/RPOPLPUSH.ts index da45f6f6024..1a5e1cc59bc 100644 --- a/packages/client/lib/commands/RPOPLPUSH.ts +++ b/packages/client/lib/commands/RPOPLPUSH.ts @@ -1,12 +1,12 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + source: RedisArgument, + destination: RedisArgument + ) { return ['RPOPLPUSH', source, destination]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPOP_COUNT.spec.ts b/packages/client/lib/commands/RPOP_COUNT.spec.ts index 3657a608039..03ede55d6b1 100644 --- a/packages/client/lib/commands/RPOP_COUNT.spec.ts +++ b/packages/client/lib/commands/RPOP_COUNT.spec.ts @@ -1,28 +1,24 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RPOP_COUNT'; +import RPOP_COUNT from './RPOP_COUNT'; describe('RPOP COUNT', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['RPOP', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RPOP_COUNT.transformArguments('key', 1), + ['RPOP', 'key', '1'] + ); + }); - testUtils.testWithClient('client.rPopCount', async client => { - assert.equal( - await client.rPopCount('key', 1), - null - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.rPopCount', async cluster => { - assert.equal( - await cluster.rPopCount('key', 1), - null - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('rPopCount', async client => { + assert.equal( + await client.rPopCount('key', 1), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RPOP_COUNT.ts b/packages/client/lib/commands/RPOP_COUNT.ts index b3bc778ee5c..b60dec6ab9d 100644 --- a/packages/client/lib/commands/RPOP_COUNT.ts +++ b/packages/client/lib/commands/RPOP_COUNT.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, count: number) { return ['RPOP', key, count.toString()]; -} - -export declare function transformReply(): Array | null; + }, + transformReply: undefined as unknown as () => ArrayReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPUSH.spec.ts b/packages/client/lib/commands/RPUSH.spec.ts index afa5c1c6400..0e860b3454f 100644 --- a/packages/client/lib/commands/RPUSH.spec.ts +++ b/packages/client/lib/commands/RPUSH.spec.ts @@ -1,35 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RPUSH'; +import RPUSH from './RPUSH'; describe('RPUSH', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'element'), - ['RPUSH', 'key', 'element'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['RPUSH', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + RPUSH.transformArguments('key', 'element'), + ['RPUSH', 'key', 'element'] + ); }); - testUtils.testWithClient('client.rPush', async client => { - assert.equal( - await client.rPush('key', 'element'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + it('array', () => { + assert.deepEqual( + RPUSH.transformArguments('key', ['1', '2']), + ['RPUSH', 'key', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.rPush', async cluster => { - assert.equal( - await cluster.rPush('key', 'element'), - 1 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('rPush', async client => { + assert.equal( + await client.rPush('key', 'element'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RPUSH.ts b/packages/client/lib/commands/RPUSH.ts index 15e282f0892..4b048777389 100644 --- a/packages/client/lib/commands/RPUSH.ts +++ b/packages/client/lib/commands/RPUSH.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['RPUSH', key], element); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + element: RedisVariadicArgument + ) { + return pushVariadicArguments(['RPUSH', key], element); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/RPUSHX.spec.ts b/packages/client/lib/commands/RPUSHX.spec.ts index ee2041de6f2..a8bd46d07c7 100644 --- a/packages/client/lib/commands/RPUSHX.spec.ts +++ b/packages/client/lib/commands/RPUSHX.spec.ts @@ -1,35 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RPUSHX'; +import RPUSHX from './RPUSHX'; describe('RPUSHX', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'element'), - ['RPUSHX', 'key', 'element'] - ); - }); - - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['RPUSHX', 'key', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + RPUSHX.transformArguments('key', 'element'), + ['RPUSHX', 'key', 'element'] + ); }); - testUtils.testWithClient('client.rPushX', async client => { - assert.equal( - await client.rPushX('key', 'element'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('array', () => { + assert.deepEqual( + RPUSHX.transformArguments('key', ['1', '2']), + ['RPUSHX', 'key', '1', '2'] + ); + }); + }); - testUtils.testWithCluster('cluster.rPushX', async cluster => { - assert.equal( - await cluster.rPushX('key', 'element'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('rPushX', async client => { + assert.equal( + await client.rPushX('key', 'element'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/RPUSHX.ts b/packages/client/lib/commands/RPUSHX.ts index 29253cd6edb..00b29624b0d 100644 --- a/packages/client/lib/commands/RPUSHX.ts +++ b/packages/client/lib/commands/RPUSHX.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - element: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['RPUSHX', key], element); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + element: RedisVariadicArgument + ) { + return pushVariadicArguments(['RPUSHX', key], element); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SADD.spec.ts b/packages/client/lib/commands/SADD.spec.ts index 4533f6f9ad5..5496c003537 100644 --- a/packages/client/lib/commands/SADD.spec.ts +++ b/packages/client/lib/commands/SADD.spec.ts @@ -1,28 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SADD'; +import SADD from './SADD'; describe('SADD', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['SADD', 'key', 'member'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SADD.transformArguments('key', 'member'), + ['SADD', 'key', 'member'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['SADD', 'key', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SADD.transformArguments('key', ['1', '2']), + ['SADD', 'key', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sAdd', async client => { - assert.equal( - await client.sAdd('key', 'member'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sAdd', async client => { + assert.equal( + await client.sAdd('key', 'member'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SADD.ts b/packages/client/lib/commands/SADD.ts index 7d7121e5391..d3f164e7af7 100644 --- a/packages/client/lib/commands/SADD.ts +++ b/packages/client/lib/commands/SADD.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - members: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SADD', key], members); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, + members: Array | RedisArgument + ) { + return pushVariadicArguments(['SADD', key], members); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SAVE.ts b/packages/client/lib/commands/SAVE.ts index 3d75c29df90..ee6cccd35a0 100644 --- a/packages/client/lib/commands/SAVE.ts +++ b/packages/client/lib/commands/SAVE.ts @@ -1,7 +1,10 @@ -import { RedisCommandArgument } from '.'; +import { SimpleStringReply, Command } from '../RESP/types'; -export function transformArguments(): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { return ['SAVE']; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCAN.spec.ts b/packages/client/lib/commands/SCAN.spec.ts index 7657b744e02..b2c10af0af1 100644 --- a/packages/client/lib/commands/SCAN.spec.ts +++ b/packages/client/lib/commands/SCAN.spec.ts @@ -1,84 +1,62 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './SCAN'; +import SCAN from './SCAN'; describe('SCAN', () => { - describe('transformArguments', () => { - it('cusror only', () => { - assert.deepEqual( - transformArguments(0), - ['SCAN', '0'] - ); - }); - - it('with MATCH', () => { - assert.deepEqual( - transformArguments(0, { - MATCH: 'pattern' - }), - ['SCAN', '0', 'MATCH', 'pattern'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - transformArguments(0, { - COUNT: 1 - }), - ['SCAN', '0', 'COUNT', '1'] - ); - }); - - it('with TYPE', () => { - assert.deepEqual( - transformArguments(0, { - TYPE: 'stream' - }), - ['SCAN', '0', 'TYPE', 'stream'] - ); - }); + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + SCAN.transformArguments(0), + ['SCAN', '0'] + ); + }); - it('with MATCH & COUNT & TYPE', () => { - assert.deepEqual( - transformArguments(0, { - MATCH: 'pattern', - COUNT: 1, - TYPE: 'stream' - }), - ['SCAN', '0', 'MATCH', 'pattern', 'COUNT', '1', 'TYPE', 'stream'] - ); - }); + it('with MATCH', () => { + assert.deepEqual( + SCAN.transformArguments(0, { + MATCH: 'pattern' + }), + ['SCAN', '0', 'MATCH', 'pattern'] + ); }); - describe('transformReply', () => { - it('without keys', () => { - assert.deepEqual( - transformReply(['0', []]), - { - cursor: 0, - keys: [] - } - ); - }); + it('with COUNT', () => { + assert.deepEqual( + SCAN.transformArguments(0, { + COUNT: 1 + }), + ['SCAN', '0', 'COUNT', '1'] + ); + }); - it('with keys', () => { - assert.deepEqual( - transformReply(['0', ['key']]), - { - cursor: 0, - keys: ['key'] - } - ); - }); + it('with TYPE', () => { + assert.deepEqual( + SCAN.transformArguments(0, { + TYPE: 'stream' + }), + ['SCAN', '0', 'TYPE', 'stream'] + ); }); - testUtils.testWithClient('client.scan', async client => { - assert.deepEqual( - await client.scan(0), - { - cursor: 0, - keys: [] - } - ); - }, GLOBAL.SERVERS.OPEN); + it('with MATCH & COUNT & TYPE', () => { + assert.deepEqual( + SCAN.transformArguments(0, { + MATCH: 'pattern', + COUNT: 1, + TYPE: 'stream' + }), + ['SCAN', '0', 'MATCH', 'pattern', 'COUNT', '1', 'TYPE', 'stream'] + ); + }); + }); + + testUtils.testWithClient('client.scan', async client => { + assert.deepEqual( + await client.scan(0), + { + cursor: 0, + keys: [] + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/SCAN.ts b/packages/client/lib/commands/SCAN.ts index ee5908eb9bd..710a7b09be2 100644 --- a/packages/client/lib/commands/SCAN.ts +++ b/packages/client/lib/commands/SCAN.ts @@ -1,34 +1,47 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ScanOptions, pushScanArguments } from './generic-transformers'; +import { CommandArguments, RedisArgument, BlobStringReply, ArrayReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; -export interface ScanCommandOptions extends ScanOptions { - TYPE?: RedisCommandArgument; +export interface ScanCommonOptions { + MATCH?: string; + COUNT?: number; } -export function transformArguments( - cursor: number, - options?: ScanCommandOptions -): RedisCommandArguments { - const args = pushScanArguments(['SCAN'], cursor, options); +export function pushScanArguments( + args: CommandArguments, + cursor: number, + options?: ScanOptions +): CommandArguments { + args.push(cursor.toString()); - if (options?.TYPE) { - args.push('TYPE', options.TYPE); - } + if (options?.MATCH) { + args.push('MATCH', options.MATCH); + } - return args; -} + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } -type ScanRawReply = [string, Array]; + return args; +} -export interface ScanReply { - cursor: number; - keys: Array; +export interface ScanOptions extends ScanCommonOptions { + TYPE?: RedisArgument; } -export function transformReply([cursor, keys]: ScanRawReply): ScanReply { +export default { + IS_READ_ONLY: true, + transformArguments(cursor: number, options?: ScanOptions) { + const args = pushScanArguments(['SCAN'], cursor, options); + + if (options?.TYPE) { + args.push('TYPE', options.TYPE); + } + + return args; + }, + transformReply([cursor, keys]: [BlobStringReply, ArrayReply]) { return { - cursor: Number(cursor), - keys + cursor: Number(cursor), + keys }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCARD.spec.ts b/packages/client/lib/commands/SCARD.spec.ts index afc21c6b00c..2d6ba32c6d5 100644 --- a/packages/client/lib/commands/SCARD.spec.ts +++ b/packages/client/lib/commands/SCARD.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SCARD'; +import SCARD from './SCARD'; describe('SCARD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['SCARD', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SCARD.transformArguments('key'), + ['SCARD', 'key'] + ); + }); - testUtils.testWithClient('client.sCard', async client => { - assert.equal( - await client.sCard('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sCard', async client => { + assert.equal( + await client.sCard('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SCARD.ts b/packages/client/lib/commands/SCARD.ts index 0d3ce49b6b2..c13d042ba60 100644 --- a/packages/client/lib/commands/SCARD.ts +++ b/packages/client/lib/commands/SCARD.ts @@ -1,7 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export function transformArguments(key: string): Array { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['SCARD', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_DEBUG.ts b/packages/client/lib/commands/SCRIPT_DEBUG.ts index e9e1e909d59..b4bc1cfa8e0 100644 --- a/packages/client/lib/commands/SCRIPT_DEBUG.ts +++ b/packages/client/lib/commands/SCRIPT_DEBUG.ts @@ -1,5 +1,10 @@ -export function transformArguments(mode: 'YES' | 'SYNC' | 'NO'): Array { - return ['SCRIPT', 'DEBUG', mode]; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(mode: 'YES' | 'SYNC' | 'NO') { + return ['SCRIPT', 'DEBUG', mode]; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_EXISTS.ts b/packages/client/lib/commands/SCRIPT_EXISTS.ts index cee889215d3..ab0a293d8de 100644 --- a/packages/client/lib/commands/SCRIPT_EXISTS.ts +++ b/packages/client/lib/commands/SCRIPT_EXISTS.ts @@ -1,8 +1,11 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { ArrayReply, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export function transformArguments(sha1: string | Array): RedisCommandArguments { - return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1); -} - -export { transformBooleanArrayReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(sha1: RedisVariadicArgument) { + return pushVariadicArguments(['SCRIPT', 'EXISTS'], sha1); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_FLUSH.ts b/packages/client/lib/commands/SCRIPT_FLUSH.ts index 2c220e9e3d1..6a224cdaa85 100644 --- a/packages/client/lib/commands/SCRIPT_FLUSH.ts +++ b/packages/client/lib/commands/SCRIPT_FLUSH.ts @@ -1,11 +1,16 @@ -export function transformArguments(mode?: 'ASYNC' | 'SYNC'): Array { +import { SimpleStringReply, Command } from '../RESP/types'; + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(mode?: 'ASYNC' | 'SYNC') { const args = ['SCRIPT', 'FLUSH']; if (mode) { - args.push(mode); + args.push(mode); } return args; -} - -export declare function transformReply(): string; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_KILL.ts b/packages/client/lib/commands/SCRIPT_KILL.ts index c0a53da8681..973bf51660d 100644 --- a/packages/client/lib/commands/SCRIPT_KILL.ts +++ b/packages/client/lib/commands/SCRIPT_KILL.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['SCRIPT', 'KILL']; -} +import { SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['SCRIPT', 'KILL']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SCRIPT_LOAD.ts b/packages/client/lib/commands/SCRIPT_LOAD.ts index 7cb28c1ec7f..90028b13a5f 100644 --- a/packages/client/lib/commands/SCRIPT_LOAD.ts +++ b/packages/client/lib/commands/SCRIPT_LOAD.ts @@ -1,5 +1,10 @@ -export function transformArguments(script: string): Array { - return ['SCRIPT', 'LOAD', script]; -} +import { BlobStringReply, Command, RedisArgument } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(script: RedisArgument) { + return ['SCRIPT', 'LOAD', script]; + }, + transformReply: undefined as unknown as () => BlobStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SDIFF.spec.ts b/packages/client/lib/commands/SDIFF.spec.ts index 340906e9350..8c614a04143 100644 --- a/packages/client/lib/commands/SDIFF.spec.ts +++ b/packages/client/lib/commands/SDIFF.spec.ts @@ -1,28 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SDIFF'; +import SDIFF from './SDIFF'; describe('SDIFF', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['SDIFF', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SDIFF.transformArguments('key'), + ['SDIFF', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['SDIFF', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SDIFF.transformArguments(['1', '2']), + ['SDIFF', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sDiff', async client => { - assert.deepEqual( - await client.sDiff('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sDiff', async client => { + assert.deepEqual( + await client.sDiff('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SDIFF.ts b/packages/client/lib/commands/SDIFF.ts index 9c4f3b4820b..895a48da260 100644 --- a/packages/client/lib/commands/SDIFF.ts +++ b/packages/client/lib/commands/SDIFF.ts @@ -1,14 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SDIFF'], keys); -} - -export declare function transformReply(): Array; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + keys: Array | RedisArgument + ) { + return pushVariadicArguments(['SDIFF'], keys); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SDIFFSTORE.spec.ts b/packages/client/lib/commands/SDIFFSTORE.spec.ts index 263b4f43f64..27b03266d9b 100644 --- a/packages/client/lib/commands/SDIFFSTORE.spec.ts +++ b/packages/client/lib/commands/SDIFFSTORE.spec.ts @@ -1,28 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SDIFFSTORE'; +import SDIFFSTORE from './SDIFFSTORE'; describe('SDIFFSTORE', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['SDIFFSTORE', 'destination', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SDIFFSTORE.transformArguments('destination', 'key'), + ['SDIFFSTORE', 'destination', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['SDIFFSTORE', 'destination', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SDIFFSTORE.transformArguments('destination', ['1', '2']), + ['SDIFFSTORE', 'destination', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sDiffStore', async client => { - assert.equal( - await client.sDiffStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sDiffStore', async client => { + assert.equal( + await client.sDiffStore('{tag}destination', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SDIFFSTORE.ts b/packages/client/lib/commands/SDIFFSTORE.ts index a927e12ef0e..526469d69ba 100644 --- a/packages/client/lib/commands/SDIFFSTORE.ts +++ b/packages/client/lib/commands/SDIFFSTORE.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - destination: RedisCommandArgument, - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SDIFFSTORE', destination], keys); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + destination: RedisArgument, + keys: Array | RedisArgument + ) { + return pushVariadicArguments(['SDIFFSTORE', destination], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SET.spec.ts b/packages/client/lib/commands/SET.spec.ts index 0b3331fd3a4..acd3de046dd 100644 --- a/packages/client/lib/commands/SET.spec.ts +++ b/packages/client/lib/commands/SET.spec.ts @@ -1,129 +1,144 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SET'; +import SET from './SET'; describe('SET', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'value'), - ['SET', 'key', 'value'] - ); - }); - - it('number', () => { - assert.deepEqual( - transformArguments('key', 0), - ['SET', 'key', '0'] - ); - }); + describe('transformArguments', () => { + describe('value', () => { + it('string', () => { + assert.deepEqual( + SET.transformArguments('key', 'value'), + ['SET', 'key', 'value'] + ); + }); + + it('number', () => { + assert.deepEqual( + SET.transformArguments('key', 0), + ['SET', 'key', '0'] + ); + }); + }); - describe('TTL', () => { - it('with EX', () => { - assert.deepEqual( - transformArguments('key', 'value', { - EX: 0 - }), - ['SET', 'key', 'value', 'EX', '0'] - ); - }); + describe('expiration', () => { + it('with expiration', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + expiration: { + type: 'EX', + value: 0 + } + }), + ['SET', 'key', 'value', 'EX', '0'] + ); + }); - it('with PX', () => { - assert.deepEqual( - transformArguments('key', 'value', { - PX: 0 - }), - ['SET', 'key', 'value', 'PX', '0'] - ); - }); + it('with EX (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + EX: 0 + }), + ['SET', 'key', 'value', 'EX', '0'] + ); + }); - it('with EXAT', () => { - assert.deepEqual( - transformArguments('key', 'value', { - EXAT: 0 - }), - ['SET', 'key', 'value', 'EXAT', '0'] - ); - }); + it('with PX (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + PX: 0 + }), + ['SET', 'key', 'value', 'PX', '0'] + ); + }); - it('with PXAT', () => { - assert.deepEqual( - transformArguments('key', 'value', { - PXAT: 0 - }), - ['SET', 'key', 'value', 'PXAT', '0'] - ); - }); + it('with EXAT (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + EXAT: 0 + }), + ['SET', 'key', 'value', 'EXAT', '0'] + ); + }); - it('with KEEPTTL', () => { - assert.deepEqual( - transformArguments('key', 'value', { - KEEPTTL: true - }), - ['SET', 'key', 'value', 'KEEPTTL'] - ); - }); - }); + it('with PXAT (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + PXAT: 0 + }), + ['SET', 'key', 'value', 'PXAT', '0'] + ); + }); - describe('Guards', () => { - it('with NX', () => { - assert.deepEqual( - transformArguments('key', 'value', { - NX: true - }), - ['SET', 'key', 'value', 'NX'] - ); - }); + it('with KEEPTTL (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + KEEPTTL: true + }), + ['SET', 'key', 'value', 'KEEPTTL'] + ); + }); + }); - it('with XX', () => { - assert.deepEqual( - transformArguments('key', 'value', { - XX: true - }), - ['SET', 'key', 'value', 'XX'] - ); - }); - }); + describe('condition', () => { + it('with condition', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + condition: 'NX' + }), + ['SET', 'key', 'value', 'NX'] + ); + }); - it('with GET', () => { - assert.deepEqual( - transformArguments('key', 'value', { - GET: true - }), - ['SET', 'key', 'value', 'GET'] - ); - }); + it('with NX (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + NX: true + }), + ['SET', 'key', 'value', 'NX'] + ); + }); - it('with EX, NX, GET', () => { - assert.deepEqual( - transformArguments('key', 'value', { - EX: 1, - NX: true, - GET: true - }), - ['SET', 'key', 'value', 'EX', '1', 'NX', 'GET'] - ); - }); + it('with XX (backwards compatibility)', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + XX: true + }), + ['SET', 'key', 'value', 'XX'] + ); + }); }); - describe('client.set', () => { - testUtils.testWithClient('simple', async client => { - assert.equal( - await client.set('key', 'value'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + it('with GET', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + GET: true + }), + ['SET', 'key', 'value', 'GET'] + ); + }); - testUtils.testWithClient('with GET on empty key', async client => { - assert.equal( - await client.set('key', 'value', { - GET: true - }), - null - ); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [6, 2] - }); + it('with expiration, condition, GET', () => { + assert.deepEqual( + SET.transformArguments('key', 'value', { + expiration: { + type: 'EX', + value: 0 + }, + condition: 'NX', + GET: true + }), + ['SET', 'key', 'value', 'EX', '0', 'NX', 'GET'] + ); }); + }); + + testUtils.testAll('set', async client => { + assert.equal( + await client.set('key', 'value'), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SET.ts b/packages/client/lib/commands/SET.ts index 08ae56552b9..f0b1efd4f0b 100644 --- a/packages/client/lib/commands/SET.ts +++ b/packages/client/lib/commands/SET.ts @@ -1,63 +1,91 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; +export interface SetOptions { + expiration?: { + type: 'EX' | 'PX' | 'EXAT' | 'PXAT'; + value: number; + } | { + type: 'KEEPTTL'; + } | 'KEEPTTL'; + /** + * @deprecated Use `expiration` { type: 'EX', value: number } instead + */ + EX?: number; + /** + * @deprecated Use `expiration` { type: 'PX', value: number } instead + */ + PX?: number; + /** + * @deprecated Use `expiration` { type: 'EXAT', value: number } instead + */ + EXAT?: number; + /** + * @deprecated Use `expiration` { type: 'PXAT', value: number } instead + */ + PXAT?: number; + /** + * @deprecated Use `expiration` 'KEEPTTL' instead + */ + KEEPTTL?: boolean; -type MaximumOneOf = - K extends keyof T ? { [P in K]?: T[K] } & Partial, never>> : never; - -type SetTTL = MaximumOneOf<{ - EX: number; - PX: number; - EXAT: number; - PXAT: number; - KEEPTTL: true; -}>; - -type SetGuards = MaximumOneOf<{ - NX: true; - XX: true; -}>; - -interface SetCommonOptions { - GET?: true; + condition?: 'NX' | 'XX'; + /** + * @deprecated Use `condition` 'NX' instead + */ + NX?: boolean; + /** + * @deprecated Use `condition` 'XX' instead + */ + XX?: boolean; + + GET?: boolean; } -export type SetOptions = SetTTL & SetGuards & SetCommonOptions; - -export function transformArguments( - key: RedisCommandArgument, - value: RedisCommandArgument | number, - options?: SetOptions -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, value: RedisArgument | number, options?: SetOptions) { const args = [ - 'SET', - key, - typeof value === 'number' ? value.toString() : value + 'SET', + key, + typeof value === 'number' ? value.toString() : value ]; - if (options?.EX !== undefined) { - args.push('EX', options.EX.toString()); + if (options?.expiration) { + if (typeof options.expiration === 'string') { + args.push(options.expiration); + } else if (options.expiration.type === 'KEEPTTL') { + args.push('KEEPTTL'); + } else { + args.push( + options.expiration.type, + options.expiration.value.toString() + ); + } + } else if (options?.EX !== undefined) { + args.push('EX', options.EX.toString()); } else if (options?.PX !== undefined) { - args.push('PX', options.PX.toString()); + args.push('PX', options.PX.toString()); } else if (options?.EXAT !== undefined) { - args.push('EXAT', options.EXAT.toString()); + args.push('EXAT', options.EXAT.toString()); } else if (options?.PXAT !== undefined) { - args.push('PXAT', options.PXAT.toString()); + args.push('PXAT', options.PXAT.toString()); } else if (options?.KEEPTTL) { - args.push('KEEPTTL'); + args.push('KEEPTTL'); } - if (options?.NX) { - args.push('NX'); + if (options?.condition) { + args.push(options.condition); + } else if (options?.NX) { + args.push('NX'); } else if (options?.XX) { - args.push('XX'); + args.push('XX'); } if (options?.GET) { - args.push('GET'); + args.push('GET'); } return args; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> | BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SETBIT.spec.ts b/packages/client/lib/commands/SETBIT.spec.ts index 43fbff7c2d9..05b817501ee 100644 --- a/packages/client/lib/commands/SETBIT.spec.ts +++ b/packages/client/lib/commands/SETBIT.spec.ts @@ -1,26 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SETBIT'; +import SETBIT from './SETBIT'; describe('SETBIT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 1), - ['SETBIT', 'key', '0', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SETBIT.transformArguments('key', 0, 1), + ['SETBIT', 'key', '0', '1'] + ); + }); - testUtils.testWithClient('client.setBit', async client => { - assert.equal( - await client.setBit('key', 0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.setBit', async cluster => { - assert.equal( - await cluster.setBit('key', 0, 1), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('setBit', async client => { + assert.equal( + await client.setBit('key', 0, 1), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SETBIT.ts b/packages/client/lib/commands/SETBIT.ts index 94f463330a8..5b3ec6173dc 100644 --- a/packages/client/lib/commands/SETBIT.ts +++ b/packages/client/lib/commands/SETBIT.ts @@ -1,14 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; import { BitValue } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, offset: number, value: BitValue -): RedisCommandArguments { + ) { return ['SETBIT', key, offset.toString(), value.toString()]; -} - -export declare function transformReply(): BitValue; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SETEX.spec.ts b/packages/client/lib/commands/SETEX.spec.ts index bca298c6c04..77034dd915a 100644 --- a/packages/client/lib/commands/SETEX.spec.ts +++ b/packages/client/lib/commands/SETEX.spec.ts @@ -1,26 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SETEX'; +import SETEX from './SETEX'; describe('SETEX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1, 'value'), - ['SETEX', 'key', '1', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SETEX.transformArguments('key', 1, 'value'), + ['SETEX', 'key', '1', 'value'] + ); + }); - testUtils.testWithClient('client.setEx', async client => { - assert.equal( - await client.setEx('key', 1, 'value'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.setEx', async cluster => { - assert.equal( - await cluster.setEx('key', 1, 'value'), - 'OK' - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('setEx', async client => { + assert.equal( + await client.setEx('key', 1, 'value'), + 'OK' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SETEX.ts b/packages/client/lib/commands/SETEX.ts index bb3068501f0..bbd77e5d990 100644 --- a/packages/client/lib/commands/SETEX.ts +++ b/packages/client/lib/commands/SETEX.ts @@ -1,18 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, seconds: number, - value: RedisCommandArgument -): RedisCommandArguments { + value: RedisArgument + ) { return [ - 'SETEX', - key, - seconds.toString(), - value + 'SETEX', + key, + seconds.toString(), + value ]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply<'OK'> +} as const satisfies Command; diff --git a/packages/client/lib/commands/SETNX .spec.ts b/packages/client/lib/commands/SETNX .spec.ts index c5bdfcffa2c..38ac5c36cf1 100644 --- a/packages/client/lib/commands/SETNX .spec.ts +++ b/packages/client/lib/commands/SETNX .spec.ts @@ -1,26 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SETNX'; +import SETNX from './SETNX'; describe('SETNX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'value'), - ['SETNX', 'key', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SETNX.transformArguments('key', 'value'), + ['SETNX', 'key', 'value'] + ); + }); - testUtils.testWithClient('client.setNX', async client => { - assert.equal( - await client.setNX('key', 'value'), - true - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.setNX', async cluster => { - assert.equal( - await cluster.setNX('key', 'value'), - true - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('setNX', async client => { + assert.equal( + await client.setNX('key', 'value'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SETNX.ts b/packages/client/lib/commands/SETNX.ts index b01d45dc32f..0940efad693 100644 --- a/packages/client/lib/commands/SETNX.ts +++ b/packages/client/lib/commands/SETNX.ts @@ -1,12 +1,9 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - value: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + transformArguments(key: RedisArgument, value: RedisArgument) { return ['SETNX', key, value]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SETRANGE.spec.ts b/packages/client/lib/commands/SETRANGE.spec.ts index 398b7730404..5da1f185503 100644 --- a/packages/client/lib/commands/SETRANGE.spec.ts +++ b/packages/client/lib/commands/SETRANGE.spec.ts @@ -1,26 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SETRANGE'; +import SETRANGE from './SETRANGE'; describe('SETRANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 'value'), - ['SETRANGE', 'key', '0', 'value'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SETRANGE.transformArguments('key', 0, 'value'), + ['SETRANGE', 'key', '0', 'value'] + ); + }); - testUtils.testWithClient('client.setRange', async client => { - assert.equal( - await client.setRange('key', 0, 'value'), - 5 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.setRange', async cluster => { - assert.equal( - await cluster.setRange('key', 0, 'value'), - 5 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('setRange', async client => { + assert.equal( + await client.setRange('key', 0, 'value'), + 5 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SETRANGE.ts b/packages/client/lib/commands/SETRANGE.ts index 038a8a5dd7f..1951a82c07d 100644 --- a/packages/client/lib/commands/SETRANGE.ts +++ b/packages/client/lib/commands/SETRANGE.ts @@ -1,13 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, offset: number, - value: RedisCommandArgument -): RedisCommandArguments { - return ['SETRANGE', key, offset.toString(), value]; -} - -export declare function transformReply(): number; + value: RedisArgument + ) { + return [ + 'SETRANGE', + key, + offset.toString(), + value + ]; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SHUTDOWN.spec.ts b/packages/client/lib/commands/SHUTDOWN.spec.ts index d58cf4443c7..caa3a5dfd0d 100644 --- a/packages/client/lib/commands/SHUTDOWN.spec.ts +++ b/packages/client/lib/commands/SHUTDOWN.spec.ts @@ -1,27 +1,27 @@ -import { strict as assert } from 'assert'; -import { transformArguments } from './SHUTDOWN'; +// import { strict as assert } from 'assert'; +// import { transformArguments } from './SHUTDOWN'; -describe('SHUTDOWN', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['SHUTDOWN'] - ); - }); +// describe('SHUTDOWN', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments(), +// ['SHUTDOWN'] +// ); +// }); - it('NOSAVE', () => { - assert.deepEqual( - transformArguments('NOSAVE'), - ['SHUTDOWN', 'NOSAVE'] - ); - }); +// it('NOSAVE', () => { +// assert.deepEqual( +// transformArguments('NOSAVE'), +// ['SHUTDOWN', 'NOSAVE'] +// ); +// }); - it('SAVE', () => { - assert.deepEqual( - transformArguments('SAVE'), - ['SHUTDOWN', 'SAVE'] - ); - }); - }); -}); +// it('SAVE', () => { +// assert.deepEqual( +// transformArguments('SAVE'), +// ['SHUTDOWN', 'SAVE'] +// ); +// }); +// }); +// }); diff --git a/packages/client/lib/commands/SHUTDOWN.ts b/packages/client/lib/commands/SHUTDOWN.ts index 1990d05a2ed..e2c544c389d 100644 --- a/packages/client/lib/commands/SHUTDOWN.ts +++ b/packages/client/lib/commands/SHUTDOWN.ts @@ -1,11 +1,11 @@ -export function transformArguments(mode?: 'NOSAVE' | 'SAVE'): Array { - const args = ['SHUTDOWN']; +// export function transformArguments(mode?: 'NOSAVE' | 'SAVE'): Array { +// const args = ['SHUTDOWN']; - if (mode) { - args.push(mode); - } +// if (mode) { +// args.push(mode); +// } - return args; -} +// return args; +// } -export declare function transformReply(): void; +// export declare function transformReply(): void; diff --git a/packages/client/lib/commands/SINTER.spec.ts b/packages/client/lib/commands/SINTER.spec.ts index 2324eac3ee8..f147abbdbb9 100644 --- a/packages/client/lib/commands/SINTER.spec.ts +++ b/packages/client/lib/commands/SINTER.spec.ts @@ -1,28 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SINTER'; +import SINTER from './SINTER'; describe('SINTER', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['SINTER', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SINTER.transformArguments('key'), + ['SINTER', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['SINTER', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SINTER.transformArguments(['1', '2']), + ['SINTER', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sInter', async client => { - assert.deepEqual( - await client.sInter('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sInter', async client => { + assert.deepEqual( + await client.sInter('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SINTER.ts b/packages/client/lib/commands/SINTER.ts index fe1feee7ade..d5c0fbc98d6 100644 --- a/packages/client/lib/commands/SINTER.ts +++ b/packages/client/lib/commands/SINTER.ts @@ -1,14 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SINTER'], keys); -} - -export declare function transformReply(): Array; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { pushVariadicArguments } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + keys: Array | RedisArgument + ) { + return pushVariadicArguments(['SINTER'], keys); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SINTERCARD.spec.ts b/packages/client/lib/commands/SINTERCARD.spec.ts index a93699f6a13..643e5114064 100644 --- a/packages/client/lib/commands/SINTERCARD.spec.ts +++ b/packages/client/lib/commands/SINTERCARD.spec.ts @@ -1,30 +1,42 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SINTERCARD'; +import SINTERCARD from './SINTERCARD'; describe('SINTERCARD', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['SINTERCARD', '2', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + SINTERCARD.transformArguments(['1', '2']), + ['SINTERCARD', '2', '1', '2'] + ); + }); + + it('with limit (backwards compatibility)', () => { + assert.deepEqual( + SINTERCARD.transformArguments(['1', '2'], 1), + ['SINTERCARD', '2', '1', '2', 'LIMIT', '1'] + ); + }); - it('with limit', () => { - assert.deepEqual( - transformArguments(['1', '2'], 1), - ['SINTERCARD', '2', '1', '2', 'LIMIT', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + SINTERCARD.transformArguments(['1', '2'], { + LIMIT: 1 + }), + ['SINTERCARD', '2', '1', '2', 'LIMIT', '1'] + ); }); + }); - testUtils.testWithClient('client.sInterCard', async client => { - assert.deepEqual( - await client.sInterCard('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sInterCard', async client => { + assert.deepEqual( + await client.sInterCard('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SINTERCARD.ts b/packages/client/lib/commands/SINTERCARD.ts index ddb7e5b00ef..41d4626249e 100644 --- a/packages/client/lib/commands/SINTERCARD.ts +++ b/packages/client/lib/commands/SINTERCARD.ts @@ -1,21 +1,26 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; +export interface SInterCardOptions { + LIMIT?: number; +} -export function transformArguments( - keys: Array | RedisCommandArgument, - limit?: number -): RedisCommandArguments { - const args = pushVerdictArgument(['SINTERCARD'], keys); +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + keys: Array | RedisArgument, + options?: SInterCardOptions | number // `number` for backwards compatibility + ) { + const args = pushVariadicArgument(['SINTERCARD'], keys); - if (limit) { - args.push('LIMIT', limit.toString()); + if (typeof options === 'number') { // backwards compatibility + args.push('LIMIT', options.toString()); + } else if (options?.LIMIT !== undefined) { + args.push('LIMIT', options.LIMIT.toString()); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SINTERSTORE.spec.ts b/packages/client/lib/commands/SINTERSTORE.spec.ts index c4a6a095e7d..7a78f5a0992 100644 --- a/packages/client/lib/commands/SINTERSTORE.spec.ts +++ b/packages/client/lib/commands/SINTERSTORE.spec.ts @@ -1,28 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SINTERSTORE'; +import SINTERSTORE from './SINTERSTORE'; describe('SINTERSTORE', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['SINTERSTORE', 'destination', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SINTERSTORE.transformArguments('destination', 'key'), + ['SINTERSTORE', 'destination', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['SINTERSTORE', 'destination', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SINTERSTORE.transformArguments('destination', ['1', '2']), + ['SINTERSTORE', 'destination', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sInterStore', async client => { - assert.equal( - await client.sInterStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sInterStore', async client => { + assert.equal( + await client.sInterStore('{tag}destination', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SINTERSTORE.ts b/packages/client/lib/commands/SINTERSTORE.ts index 02bf9d061a0..744e0b18456 100644 --- a/packages/client/lib/commands/SINTERSTORE.ts +++ b/packages/client/lib/commands/SINTERSTORE.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - destination: RedisCommandArgument, - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SINTERSTORE', destination], keys); -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + destination: RedisArgument, + keys: RedisVariadicArgument + ) { + return pushVariadicArguments(['SINTERSTORE', destination], keys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SISMEMBER.spec.ts b/packages/client/lib/commands/SISMEMBER.spec.ts index 8d18c83697a..c4b1502230e 100644 --- a/packages/client/lib/commands/SISMEMBER.spec.ts +++ b/packages/client/lib/commands/SISMEMBER.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SISMEMBER'; +import SISMEMBER from './SISMEMBER'; describe('SISMEMBER', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['SISMEMBER', 'key', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SISMEMBER.transformArguments('key', 'member'), + ['SISMEMBER', 'key', 'member'] + ); + }); - testUtils.testWithClient('client.sIsMember', async client => { - assert.equal( - await client.sIsMember('key', 'member'), - false - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sIsMember', async client => { + assert.equal( + await client.sIsMember('key', 'member'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SISMEMBER.ts b/packages/client/lib/commands/SISMEMBER.ts index 4d40c63250e..0687d19de30 100644 --- a/packages/client/lib/commands/SISMEMBER.ts +++ b/packages/client/lib/commands/SISMEMBER.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, member: RedisArgument) { return ['SISMEMBER', key, member]; -} - -export { transformBooleanReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SMEMBERS.spec.ts b/packages/client/lib/commands/SMEMBERS.spec.ts index b9c58c9eebb..6ca8ce6597f 100644 --- a/packages/client/lib/commands/SMEMBERS.spec.ts +++ b/packages/client/lib/commands/SMEMBERS.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SMEMBERS'; +import SMEMBERS from './SMEMBERS'; describe('SMEMBERS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['SMEMBERS', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SMEMBERS.transformArguments('key'), + ['SMEMBERS', 'key'] + ); + }); - testUtils.testWithClient('client.sMembers', async client => { - assert.deepEqual( - await client.sMembers('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sMembers', async client => { + assert.deepEqual( + await client.sMembers('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SMEMBERS.ts b/packages/client/lib/commands/SMEMBERS.ts index 7950a4c073a..fdab118e86e 100644 --- a/packages/client/lib/commands/SMEMBERS.ts +++ b/packages/client/lib/commands/SMEMBERS.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SetReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['SMEMBERS', key]; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => SetReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SMISMEMBER.spec.ts b/packages/client/lib/commands/SMISMEMBER.spec.ts index e3728134029..92619820d63 100644 --- a/packages/client/lib/commands/SMISMEMBER.spec.ts +++ b/packages/client/lib/commands/SMISMEMBER.spec.ts @@ -1,21 +1,24 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SMISMEMBER'; +import SMISMEMBER from './SMISMEMBER'; describe('SMISMEMBER', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['SMISMEMBER', 'key', '1', '2'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SMISMEMBER.transformArguments('key', ['1', '2']), + ['SMISMEMBER', 'key', '1', '2'] + ); + }); - testUtils.testWithClient('client.smIsMember', async client => { - assert.deepEqual( - await client.smIsMember('key', ['1', '2']), - [false, false] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('smIsMember', async client => { + assert.deepEqual( + await client.smIsMember('key', ['1', '2']), + [0, 0] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SMISMEMBER.ts b/packages/client/lib/commands/SMISMEMBER.ts index 175120bdfb9..bdf48d45ab4 100644 --- a/packages/client/lib/commands/SMISMEMBER.ts +++ b/packages/client/lib/commands/SMISMEMBER.ts @@ -1,12 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - members: Array -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, members: Array) { return ['SMISMEMBER', key, ...members]; -} - -export { transformBooleanArrayReply as transformReply } from './generic-transformers'; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SMOVE.spec.ts b/packages/client/lib/commands/SMOVE.spec.ts index e3308ee8143..d3a9ca6eda6 100644 --- a/packages/client/lib/commands/SMOVE.spec.ts +++ b/packages/client/lib/commands/SMOVE.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SMOVE'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './SMOVE'; -describe('SMOVE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('source', 'destination', 'member'), - ['SMOVE', 'source', 'destination', 'member'] - ); - }); +// describe('SMOVE', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('source', 'destination', 'member'), +// ['SMOVE', 'source', 'destination', 'member'] +// ); +// }); - testUtils.testWithClient('client.sMove', async client => { - assert.equal( - await client.sMove('source', 'destination', 'member'), - false - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.sMove', async client => { +// assert.equal( +// await client.sMove('source', 'destination', 'member'), +// false +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/SMOVE.ts b/packages/client/lib/commands/SMOVE.ts index 83c4027dbd5..d6759c62122 100644 --- a/packages/client/lib/commands/SMOVE.ts +++ b/packages/client/lib/commands/SMOVE.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments( - source: RedisCommandArgument, - destination: RedisCommandArgument, - member: RedisCommandArgument -): RedisCommandArguments { - return ['SMOVE', source, destination, member]; -} +// export function transformArguments( +// source: RedisCommandArgument, +// destination: RedisCommandArgument, +// member: RedisCommandArgument +// ): RedisCommandArguments { +// return ['SMOVE', source, destination, member]; +// } -export { transformBooleanReply as transformReply } from './generic-transformers'; +// export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/SORT.spec.ts b/packages/client/lib/commands/SORT.spec.ts index 4967b020ad5..58540b3f2f8 100644 --- a/packages/client/lib/commands/SORT.spec.ts +++ b/packages/client/lib/commands/SORT.spec.ts @@ -1,96 +1,99 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SORT'; +import SORT from './SORT'; describe('SORT', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key'), - ['SORT', 'key'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + SORT.transformArguments('key'), + ['SORT', 'key'] + ); + }); - it('with BY', () => { - assert.deepEqual( - transformArguments('key', { - BY: 'pattern' - }), - ['SORT', 'key', 'BY', 'pattern'] - ); - }); + it('with BY', () => { + assert.deepEqual( + SORT.transformArguments('key', { + BY: 'pattern' + }), + ['SORT', 'key', 'BY', 'pattern'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('key', { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['SORT', 'key', 'LIMIT', '0', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + SORT.transformArguments('key', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['SORT', 'key', 'LIMIT', '0', '1'] + ); + }); - describe('with GET', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', { - GET: 'pattern' - }), - ['SORT', 'key', 'GET', 'pattern'] - ); - }); + describe('with GET', () => { + it('string', () => { + assert.deepEqual( + SORT.transformArguments('key', { + GET: 'pattern' + }), + ['SORT', 'key', 'GET', 'pattern'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', { - GET: ['1', '2'] - }), - ['SORT', 'key', 'GET', '1', 'GET', '2'] - ); - }); - }); + it('array', () => { + assert.deepEqual( + SORT.transformArguments('key', { + GET: ['1', '2'] + }), + ['SORT', 'key', 'GET', '1', 'GET', '2'] + ); + }); + }); - it('with DIRECTION', () => { - assert.deepEqual( - transformArguments('key', { - DIRECTION: 'ASC' - }), - ['SORT', 'key', 'ASC'] - ); - }); + it('with DIRECTION', () => { + assert.deepEqual( + SORT.transformArguments('key', { + DIRECTION: 'ASC' + }), + ['SORT', 'key', 'ASC'] + ); + }); - it('with ALPHA', () => { - assert.deepEqual( - transformArguments('key', { - ALPHA: true - }), - ['SORT', 'key', 'ALPHA'] - ); - }); + it('with ALPHA', () => { + assert.deepEqual( + SORT.transformArguments('key', { + ALPHA: true + }), + ['SORT', 'key', 'ALPHA'] + ); + }); - it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { - assert.deepEqual( - transformArguments('key', { - BY: 'pattern', - LIMIT: { - offset: 0, - count: 1 - }, - GET: 'pattern', - DIRECTION: 'ASC', - ALPHA: true - }), - ['SORT', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA'] - ); - }); + it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { + assert.deepEqual( + SORT.transformArguments('key', { + BY: 'pattern', + LIMIT: { + offset: 0, + count: 1 + }, + GET: 'pattern', + DIRECTION: 'ASC', + ALPHA: true + }), + ['SORT', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA'] + ); }); + }); - testUtils.testWithClient('client.sort', async client => { - assert.deepEqual( - await client.sort('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sort', async client => { + assert.deepEqual( + await client.sort('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SORT.ts b/packages/client/lib/commands/SORT.ts index 15e95bde677..b71383943e9 100644 --- a/packages/client/lib/commands/SORT.ts +++ b/packages/client/lib/commands/SORT.ts @@ -1,13 +1,59 @@ -import { RedisCommandArguments } from '.'; -import { pushSortArguments, SortOptions } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; +export interface SortOptions { + BY?: RedisArgument; + LIMIT?: { + offset: number; + count: number; + }; + GET?: RedisArgument | Array; + DIRECTION?: 'ASC' | 'DESC'; + ALPHA?: boolean; +} + +export function transformSortArguments( + command: RedisArgument, + key: RedisArgument, + options?: SortOptions +) { + const args: Array = [command, key]; + + if (options?.BY) { + args.push('BY', options.BY); + } + + if (options?.LIMIT) { + args.push( + 'LIMIT', + options.LIMIT.offset.toString(), + options.LIMIT.count.toString() + ); + } + + if (options?.GET) { + if (Array.isArray(options.GET)) { + for (const pattern of options.GET) { + args.push('GET', pattern); + } + } else { + args.push('GET', options.GET); + } + } + + if (options?.DIRECTION) { + args.push(options.DIRECTION); + } + + if (options?.ALPHA) { + args.push('ALPHA'); + } -export function transformArguments( - key: string, - options?: SortOptions -): RedisCommandArguments { - return pushSortArguments(['SORT', key], options); + return args; } -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: transformSortArguments.bind(undefined, 'SORT'), + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SORT_RO.spec.ts b/packages/client/lib/commands/SORT_RO.spec.ts index fe3ca1240d7..d1349df2274 100644 --- a/packages/client/lib/commands/SORT_RO.spec.ts +++ b/packages/client/lib/commands/SORT_RO.spec.ts @@ -1,98 +1,101 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SORT_RO'; +import SORT_RO from './SORT_RO'; describe('SORT_RO', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key'), - ['SORT_RO', 'key'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + SORT_RO.transformArguments('key'), + ['SORT_RO', 'key'] + ); + }); - it('with BY', () => { - assert.deepEqual( - transformArguments('key', { - BY: 'pattern' - }), - ['SORT_RO', 'key', 'BY', 'pattern'] - ); - }); + it('with BY', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + BY: 'pattern' + }), + ['SORT_RO', 'key', 'BY', 'pattern'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('key', { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['SORT_RO', 'key', 'LIMIT', '0', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['SORT_RO', 'key', 'LIMIT', '0', '1'] + ); + }); - describe('with GET', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', { - GET: 'pattern' - }), - ['SORT_RO', 'key', 'GET', 'pattern'] - ); - }); + describe('with GET', () => { + it('string', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + GET: 'pattern' + }), + ['SORT_RO', 'key', 'GET', 'pattern'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', { - GET: ['1', '2'] - }), - ['SORT_RO', 'key', 'GET', '1', 'GET', '2'] - ); - }); - }); + it('array', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + GET: ['1', '2'] + }), + ['SORT_RO', 'key', 'GET', '1', 'GET', '2'] + ); + }); + }); - it('with DIRECTION', () => { - assert.deepEqual( - transformArguments('key', { - DIRECTION: 'ASC' - }), - ['SORT_RO', 'key', 'ASC'] - ); - }); + it('with DIRECTION', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + DIRECTION: 'ASC' + }), + ['SORT_RO', 'key', 'ASC'] + ); + }); - it('with ALPHA', () => { - assert.deepEqual( - transformArguments('key', { - ALPHA: true - }), - ['SORT_RO', 'key', 'ALPHA'] - ); - }); + it('with ALPHA', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + ALPHA: true + }), + ['SORT_RO', 'key', 'ALPHA'] + ); + }); - it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { - assert.deepEqual( - transformArguments('key', { - BY: 'pattern', - LIMIT: { - offset: 0, - count: 1 - }, - GET: 'pattern', - DIRECTION: 'ASC', - ALPHA: true, - }), - ['SORT_RO', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA'] - ); - }); + it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { + assert.deepEqual( + SORT_RO.transformArguments('key', { + BY: 'pattern', + LIMIT: { + offset: 0, + count: 1 + }, + GET: 'pattern', + DIRECTION: 'ASC', + ALPHA: true, + }), + ['SORT_RO', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA'] + ); }); + }); - testUtils.testWithClient('client.sortRo', async client => { - assert.deepEqual( - await client.sortRo('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('client.sortRo', async client => { + assert.deepEqual( + await client.sortRo('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SORT_RO.ts b/packages/client/lib/commands/SORT_RO.ts index 4af7acd80d7..459a0bbc03d 100644 --- a/packages/client/lib/commands/SORT_RO.ts +++ b/packages/client/lib/commands/SORT_RO.ts @@ -1,15 +1,9 @@ -import { RedisCommandArguments } from '.'; -import { pushSortArguments, SortOptions } from "./generic-transformers"; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: string, - options?: SortOptions -): RedisCommandArguments { - return pushSortArguments(['SORT_RO', key], options); -} - -export declare function transformReply(): Array; +import { Command } from '../RESP/types'; +import SORT, { transformSortArguments } from './SORT'; + +export default { + FIRST_KEY_INDEX: SORT.FIRST_KEY_INDEX, + IS_READ_ONLY: true, + transformArguments: transformSortArguments.bind(undefined, 'SORT_RO'), + transformReply: SORT.transformReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SORT_STORE.spec.ts b/packages/client/lib/commands/SORT_STORE.spec.ts index d078135255d..1d1152852b2 100644 --- a/packages/client/lib/commands/SORT_STORE.spec.ts +++ b/packages/client/lib/commands/SORT_STORE.spec.ts @@ -1,96 +1,99 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SORT_STORE'; +import SORT_STORE from './SORT_STORE'; describe('SORT STORE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('source', 'destination'), - ['SORT', 'source', 'STORE', 'destination'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination'), + ['SORT', 'source', 'STORE', 'destination'] + ); + }); - it('with BY', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - BY: 'pattern' - }), - ['SORT', 'source', 'BY', 'pattern', 'STORE', 'destination'] - ); - }); + it('with BY', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + BY: 'pattern' + }), + ['SORT', 'source', 'BY', 'pattern', 'STORE', 'destination'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['SORT', 'source', 'LIMIT', '0', '1', 'STORE', 'destination'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['SORT', 'source', 'LIMIT', '0', '1', 'STORE', 'destination'] + ); + }); - describe('with GET', () => { - it('string', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - GET: 'pattern' - }), - ['SORT', 'source', 'GET', 'pattern', 'STORE', 'destination'] - ); - }); + describe('with GET', () => { + it('string', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + GET: 'pattern' + }), + ['SORT', 'source', 'GET', 'pattern', 'STORE', 'destination'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - GET: ['1', '2'] - }), - ['SORT', 'source', 'GET', '1', 'GET', '2', 'STORE', 'destination'] - ); - }); - }); + it('array', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + GET: ['1', '2'] + }), + ['SORT', 'source', 'GET', '1', 'GET', '2', 'STORE', 'destination'] + ); + }); + }); - it('with DIRECTION', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - DIRECTION: 'ASC' - }), - ['SORT', 'source', 'ASC', 'STORE', 'destination'] - ); - }); + it('with DIRECTION', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + DIRECTION: 'ASC' + }), + ['SORT', 'source', 'ASC', 'STORE', 'destination'] + ); + }); - it('with ALPHA', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - ALPHA: true - }), - ['SORT', 'source', 'ALPHA', 'STORE', 'destination'] - ); - }); + it('with ALPHA', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + ALPHA: true + }), + ['SORT', 'source', 'ALPHA', 'STORE', 'destination'] + ); + }); - it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { - assert.deepEqual( - transformArguments('source', 'destination', { - BY: 'pattern', - LIMIT: { - offset: 0, - count: 1 - }, - GET: 'pattern', - DIRECTION: 'ASC', - ALPHA: true - }), - ['SORT', 'source', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA', 'STORE', 'destination'] - ); - }); + it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => { + assert.deepEqual( + SORT_STORE.transformArguments('source', 'destination', { + BY: 'pattern', + LIMIT: { + offset: 0, + count: 1 + }, + GET: 'pattern', + DIRECTION: 'ASC', + ALPHA: true + }), + ['SORT', 'source', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA', 'STORE', 'destination'] + ); }); + }); - testUtils.testWithClient('client.sortStore', async client => { - assert.equal( - await client.sortStore('source', 'destination'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sortStore', async client => { + assert.equal( + await client.sortStore('{tag}source', '{tag}destination'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SORT_STORE.ts b/packages/client/lib/commands/SORT_STORE.ts index 9acaf023175..b6ad709fb60 100644 --- a/packages/client/lib/commands/SORT_STORE.ts +++ b/packages/client/lib/commands/SORT_STORE.ts @@ -1,17 +1,17 @@ -import { RedisCommandArguments } from '.'; -import { SortOptions } from './generic-transformers'; -import { transformArguments as transformSortArguments } from './SORT'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import SORT, { SortOptions } from './SORT'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - source: string, - destination: string, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + source: RedisArgument, + destination: RedisArgument, options?: SortOptions -): RedisCommandArguments { - const args = transformSortArguments(source, options); + ) { + const args = SORT.transformArguments(source, options); args.push('STORE', destination); return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SPOP.spec.ts b/packages/client/lib/commands/SPOP.spec.ts index 6a384d181fc..1800d08622d 100644 --- a/packages/client/lib/commands/SPOP.spec.ts +++ b/packages/client/lib/commands/SPOP.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SPOP'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './SPOP'; -describe('SPOP', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key'), - ['SPOP', 'key'] - ); - }); +// describe('SPOP', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('key'), +// ['SPOP', 'key'] +// ); +// }); - it('with count', () => { - assert.deepEqual( - transformArguments('key', 2), - ['SPOP', 'key', '2'] - ); - }); - }); +// it('with count', () => { +// assert.deepEqual( +// transformArguments('key', 2), +// ['SPOP', 'key', '2'] +// ); +// }); +// }); - testUtils.testWithClient('client.sPop', async client => { - assert.equal( - await client.sPop('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.sPop', async client => { +// assert.equal( +// await client.sPop('key'), +// null +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/SPOP.ts b/packages/client/lib/commands/SPOP.ts index 38ce8573f3f..3566cd13dc5 100644 --- a/packages/client/lib/commands/SPOP.ts +++ b/packages/client/lib/commands/SPOP.ts @@ -1,18 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments( - key: RedisCommandArgument, - count?: number -): RedisCommandArguments { - const args = ['SPOP', key]; +// export function transformArguments( +// key: RedisCommandArgument, +// count?: number +// ): RedisCommandArguments { +// const args = ['SPOP', key]; - if (typeof count === 'number') { - args.push(count.toString()); - } +// if (typeof count === 'number') { +// args.push(count.toString()); +// } - return args; -} +// return args; +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SPUBLISH.spec.ts b/packages/client/lib/commands/SPUBLISH.spec.ts index 60b6ce2dad0..0b1391774b0 100644 --- a/packages/client/lib/commands/SPUBLISH.spec.ts +++ b/packages/client/lib/commands/SPUBLISH.spec.ts @@ -1,21 +1,24 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SPUBLISH'; +import SPUBLISH from './SPUBLISH'; describe('SPUBLISH', () => { - testUtils.isVersionGreaterThanHook([7]); - - it('transformArguments', () => { - assert.deepEqual( - transformArguments('channel', 'message'), - ['SPUBLISH', 'channel', 'message'] - ); - }); + testUtils.isVersionGreaterThanHook([7]); - testUtils.testWithClient('client.sPublish', async client => { - assert.equal( - await client.sPublish('channel', 'message'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + it('transformArguments', () => { + assert.deepEqual( + SPUBLISH.transformArguments('channel', 'message'), + ['SPUBLISH', 'channel', 'message'] + ); + }); + + testUtils.testAll('sPublish', async client => { + assert.equal( + await client.sPublish('channel', 'message'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SPUBLISH.ts b/packages/client/lib/commands/SPUBLISH.ts index 42a7ab49072..19d84b03c6f 100644 --- a/packages/client/lib/commands/SPUBLISH.ts +++ b/packages/client/lib/commands/SPUBLISH.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const IS_READ_ONLY = true; - -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - channel: RedisCommandArgument, - message: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(channel: RedisArgument, message: RedisArgument) { return ['SPUBLISH', channel, message]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SRANDMEMBER.spec.ts b/packages/client/lib/commands/SRANDMEMBER.spec.ts index 291271540be..a0983e4480d 100644 --- a/packages/client/lib/commands/SRANDMEMBER.spec.ts +++ b/packages/client/lib/commands/SRANDMEMBER.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SRANDMEMBER'; +import SRANDMEMBER from './SRANDMEMBER'; describe('SRANDMEMBER', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['SRANDMEMBER', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SRANDMEMBER.transformArguments('key'), + ['SRANDMEMBER', 'key'] + ); + }); - testUtils.testWithClient('client.sRandMember', async client => { - assert.equal( - await client.sRandMember('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sRandMember', async client => { + assert.equal( + await client.sRandMember('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SRANDMEMBER.ts b/packages/client/lib/commands/SRANDMEMBER.ts index d84e61993e5..6a2373ae927 100644 --- a/packages/client/lib/commands/SRANDMEMBER.ts +++ b/packages/client/lib/commands/SRANDMEMBER.ts @@ -1,9 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['SRANDMEMBER', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts index d3d787b3e63..6ed30cd14ab 100644 --- a/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts +++ b/packages/client/lib/commands/SRANDMEMBER_COUNT.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SRANDMEMBER_COUNT'; +import SRANDMEMBER_COUNT from './SRANDMEMBER_COUNT'; describe('SRANDMEMBER COUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['SRANDMEMBER', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + SRANDMEMBER_COUNT.transformArguments('key', 1), + ['SRANDMEMBER', 'key', '1'] + ); + }); - testUtils.testWithClient('client.sRandMemberCount', async client => { - assert.deepEqual( - await client.sRandMemberCount('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('sRandMemberCount', async client => { + assert.deepEqual( + await client.sRandMemberCount('key', 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SRANDMEMBER_COUNT.ts b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts index d265d89e9a6..778f3d8f629 100644 --- a/packages/client/lib/commands/SRANDMEMBER_COUNT.ts +++ b/packages/client/lib/commands/SRANDMEMBER_COUNT.ts @@ -1,16 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformSRandMemberArguments } from './SRANDMEMBER'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import SRANDMEMBER from './SRANDMEMBER'; -export { FIRST_KEY_INDEX } from './SRANDMEMBER'; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformSRandMemberArguments(key), - count.toString() - ]; -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: SRANDMEMBER.FIRST_KEY_INDEX, + IS_READ_ONLY: SRANDMEMBER.IS_READ_ONLY, + transformArguments(key: RedisArgument, count: number) { + const args = SRANDMEMBER.transformArguments(key); + args.push(count.toString()); + return args; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SREM.spec.ts b/packages/client/lib/commands/SREM.spec.ts index d53d7b0334d..8df39102bc7 100644 --- a/packages/client/lib/commands/SREM.spec.ts +++ b/packages/client/lib/commands/SREM.spec.ts @@ -1,28 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SREM'; +import SREM from './SREM'; describe('SREM', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['SREM', 'key', 'member'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + SREM.transformArguments('key', 'member'), + ['SREM', 'key', 'member'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['SREM', 'key', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + SREM.transformArguments('key', ['1', '2']), + ['SREM', 'key', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.sRem', async client => { - assert.equal( - await client.sRem('key', 'member'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('client.sRem', async client => { + assert.equal( + await client.sRem('key', 'member'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/SREM.ts b/packages/client/lib/commands/SREM.ts index 34aebdf02e3..daa95493d02 100644 --- a/packages/client/lib/commands/SREM.ts +++ b/packages/client/lib/commands/SREM.ts @@ -1,13 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command, RedisArgument } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - members: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SREM', key], members); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisArgument, members: RedisVariadicArgument) { + return pushVariadicArguments(['SREM', key], members); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SSCAN.spec.ts b/packages/client/lib/commands/SSCAN.spec.ts index 71a90bf81d8..4cfb59445d1 100644 --- a/packages/client/lib/commands/SSCAN.spec.ts +++ b/packages/client/lib/commands/SSCAN.spec.ts @@ -1,74 +1,52 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './SSCAN'; +import SSCAN from './SSCAN'; describe('SSCAN', () => { - describe('transformArguments', () => { - it('cusror only', () => { - assert.deepEqual( - transformArguments('key', 0), - ['SSCAN', 'key', '0'] - ); - }); - - it('with MATCH', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern' - }), - ['SSCAN', 'key', '0', 'MATCH', 'pattern'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - COUNT: 1 - }), - ['SSCAN', 'key', '0', 'COUNT', '1'] - ); - }); + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + SSCAN.transformArguments('key', 0), + ['SSCAN', 'key', '0'] + ); + }); - it('with MATCH & COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern', - COUNT: 1 - }), - ['SSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] - ); - }); + it('with MATCH', () => { + assert.deepEqual( + SSCAN.transformArguments('key', 0, { + MATCH: 'pattern' + }), + ['SSCAN', 'key', '0', 'MATCH', 'pattern'] + ); }); - describe('transformReply', () => { - it('without members', () => { - assert.deepEqual( - transformReply(['0', []]), - { - cursor: 0, - members: [] - } - ); - }); + it('with COUNT', () => { + assert.deepEqual( + SSCAN.transformArguments('key', 0, { + COUNT: 1 + }), + ['SSCAN', 'key', '0', 'COUNT', '1'] + ); + }); - it('with members', () => { - assert.deepEqual( - transformReply(['0', ['member']]), - { - cursor: 0, - members: ['member'] - } - ); - }); + it('with MATCH & COUNT', () => { + assert.deepEqual( + SSCAN.transformArguments('key', 0, { + MATCH: 'pattern', + COUNT: 1 + }), + ['SSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] + ); }); + }); - testUtils.testWithClient('client.sScan', async client => { - assert.deepEqual( - await client.sScan('key', 0), - { - cursor: 0, - members: [] - } - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.sScan', async client => { + assert.deepEqual( + await client.sScan('key', 0), + { + cursor: 0, + members: [] + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/SSCAN.ts b/packages/client/lib/commands/SSCAN.ts index 9b3938f159b..c36bc52ca1e 100644 --- a/packages/client/lib/commands/SSCAN.ts +++ b/packages/client/lib/commands/SSCAN.ts @@ -1,31 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ScanOptions, pushScanArguments } from './generic-transformers'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; +import { ScanCommonOptions, pushScanArguments } from './SCAN'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, cursor: number, - options?: ScanOptions -): RedisCommandArguments { - return pushScanArguments([ - 'SSCAN', - key, - ], cursor, options); -} - -type SScanRawReply = [string, Array]; - -interface SScanReply { - cursor: number; - members: Array; -} - -export function transformReply([cursor, members]: SScanRawReply): SScanReply { + options?: ScanCommonOptions + ) { + return pushScanArguments(['SSCAN', key], cursor, options); + }, + transformReply([cursor, members]: [BlobStringReply, Array]) { return { - cursor: Number(cursor), - members + cursor: Number(cursor), + members }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/STRLEN.spec.ts b/packages/client/lib/commands/STRLEN.spec.ts index 519c68d3e5d..b80a84a010d 100644 --- a/packages/client/lib/commands/STRLEN.spec.ts +++ b/packages/client/lib/commands/STRLEN.spec.ts @@ -1,26 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './STRLEN'; +import STRLEN from './STRLEN'; describe('STRLEN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['STRLEN', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + STRLEN.transformArguments('key'), + ['STRLEN', 'key'] + ); + }); - testUtils.testWithClient('client.strLen', async client => { - assert.equal( - await client.strLen('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithCluster('cluster.strLen', async cluster => { - assert.equal( - await cluster.strLen('key'), - 0 - ); - }, GLOBAL.CLUSTERS.OPEN); + testUtils.testAll('strLen', async client => { + assert.equal( + await client.strLen('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/STRLEN.ts b/packages/client/lib/commands/STRLEN.ts index de88340d8b6..594530ff6bf 100644 --- a/packages/client/lib/commands/STRLEN.ts +++ b/packages/client/lib/commands/STRLEN.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['STRLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/SUNION.spec.ts b/packages/client/lib/commands/SUNION.spec.ts index 2918607c1d6..b0ddf782f9d 100644 --- a/packages/client/lib/commands/SUNION.spec.ts +++ b/packages/client/lib/commands/SUNION.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUNION'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './SUNION'; -describe('SUNION', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['SUNION', 'key'] - ); - }); +// describe('SUNION', () => { +// describe('transformArguments', () => { +// it('string', () => { +// assert.deepEqual( +// transformArguments('key'), +// ['SUNION', 'key'] +// ); +// }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['SUNION', '1', '2'] - ); - }); - }); +// it('array', () => { +// assert.deepEqual( +// transformArguments(['1', '2']), +// ['SUNION', '1', '2'] +// ); +// }); +// }); - testUtils.testWithClient('client.sUnion', async client => { - assert.deepEqual( - await client.sUnion('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.sUnion', async client => { +// assert.deepEqual( +// await client.sUnion('key'), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/SUNION.ts b/packages/client/lib/commands/SUNION.ts index 52c112e6610..f9e7172acbb 100644 --- a/packages/client/lib/commands/SUNION.ts +++ b/packages/client/lib/commands/SUNION.ts @@ -1,14 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments( - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SUNION'], keys); -} +// export function transformArguments( +// keys: RedisCommandArgument | Array +// ): RedisCommandArguments { +// return pushVariadicArguments(['SUNION'], keys); +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/SUNIONSTORE.spec.ts b/packages/client/lib/commands/SUNIONSTORE.spec.ts index 142533eea2b..4cf50132aa2 100644 --- a/packages/client/lib/commands/SUNIONSTORE.spec.ts +++ b/packages/client/lib/commands/SUNIONSTORE.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SUNIONSTORE'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './SUNIONSTORE'; -describe('SUNIONSTORE', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['SUNIONSTORE', 'destination', 'key'] - ); - }); +// describe('SUNIONSTORE', () => { +// describe('transformArguments', () => { +// it('string', () => { +// assert.deepEqual( +// transformArguments('destination', 'key'), +// ['SUNIONSTORE', 'destination', 'key'] +// ); +// }); - it('array', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['SUNIONSTORE', 'destination', '1', '2'] - ); - }); - }); +// it('array', () => { +// assert.deepEqual( +// transformArguments('destination', ['1', '2']), +// ['SUNIONSTORE', 'destination', '1', '2'] +// ); +// }); +// }); - testUtils.testWithClient('client.sUnionStore', async client => { - assert.equal( - await client.sUnionStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.sUnionStore', async client => { +// assert.equal( +// await client.sUnionStore('destination', 'key'), +// 0 +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/SUNIONSTORE.ts b/packages/client/lib/commands/SUNIONSTORE.ts index 94df6771a04..1d5ee097caf 100644 --- a/packages/client/lib/commands/SUNIONSTORE.ts +++ b/packages/client/lib/commands/SUNIONSTORE.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments( - destination: RedisCommandArgument, - keys: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['SUNIONSTORE', destination], keys); -} +// export function transformArguments( +// destination: RedisCommandArgument, +// keys: RedisCommandArgument | Array +// ): RedisCommandArguments { +// return pushVariadicArguments(['SUNIONSTORE', destination], keys); +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/SWAPDB.spec.ts b/packages/client/lib/commands/SWAPDB.spec.ts index add87512a64..dee881b5dc5 100644 --- a/packages/client/lib/commands/SWAPDB.spec.ts +++ b/packages/client/lib/commands/SWAPDB.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './SWAPDB'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './SWAPDB'; -describe('SWAPDB', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(0, 1), - ['SWAPDB', '0', '1'] - ); - }); +// describe('SWAPDB', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments(0, 1), +// ['SWAPDB', '0', '1'] +// ); +// }); - testUtils.testWithClient('client.swapDb', async client => { - assert.equal( - await client.swapDb(0, 1), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.swapDb', async client => { +// assert.equal( +// await client.swapDb(0, 1), +// 'OK' +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/SWAPDB.ts b/packages/client/lib/commands/SWAPDB.ts index 7f13d6b008e..0ab785fb594 100644 --- a/packages/client/lib/commands/SWAPDB.ts +++ b/packages/client/lib/commands/SWAPDB.ts @@ -1,5 +1,5 @@ -export function transformArguments(index1: number, index2: number): Array { - return ['SWAPDB', index1.toString(), index2.toString()]; -} +// export function transformArguments(index1: number, index2: number): Array { +// return ['SWAPDB', index1.toString(), index2.toString()]; +// } -export declare function transformReply(): string; +// export declare function transformReply(): string; diff --git a/packages/client/lib/commands/TIME.spec.ts b/packages/client/lib/commands/TIME.spec.ts index bbaa7942db0..5bfd1e50d9c 100644 --- a/packages/client/lib/commands/TIME.spec.ts +++ b/packages/client/lib/commands/TIME.spec.ts @@ -1,18 +1,18 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './TIME'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './TIME'; -describe('TIME', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['TIME'] - ); - }); +// describe('TIME', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments(), +// ['TIME'] +// ); +// }); - testUtils.testWithClient('client.time', async client => { - const reply = await client.time(); - assert.ok(reply instanceof Date); - assert.ok(typeof reply.microseconds === 'number'); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.time', async client => { +// const reply = await client.time(); +// assert.ok(reply instanceof Date); +// assert.ok(typeof reply.microseconds === 'number'); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/TIME.ts b/packages/client/lib/commands/TIME.ts index 1a364d6d8be..298a4d5aee2 100644 --- a/packages/client/lib/commands/TIME.ts +++ b/packages/client/lib/commands/TIME.ts @@ -1,15 +1,15 @@ -export function transformArguments(): Array { - return ['TIME']; -} +// export function transformArguments(): Array { +// return ['TIME']; +// } -interface TimeReply extends Date { - microseconds: number; -} +// interface TimeReply extends Date { +// microseconds: number; +// } -export function transformReply(reply: [string, string]): TimeReply { - const seconds = Number(reply[0]), - microseconds = Number(reply[1]), - d: Partial = new Date(seconds * 1000 + microseconds / 1000); - d.microseconds = microseconds; - return d as TimeReply; -} +// export function transformReply(reply: [string, string]): TimeReply { +// const seconds = Number(reply[0]), +// microseconds = Number(reply[1]), +// d: Partial = new Date(seconds * 1000 + microseconds / 1000); +// d.microseconds = microseconds; +// return d as TimeReply; +// } diff --git a/packages/client/lib/commands/TOUCH.spec.ts b/packages/client/lib/commands/TOUCH.spec.ts index 578c49587d7..01c5ed9454b 100644 --- a/packages/client/lib/commands/TOUCH.spec.ts +++ b/packages/client/lib/commands/TOUCH.spec.ts @@ -1,28 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './TOUCH'; +import TOUCH from './TOUCH'; describe('TOUCH', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['TOUCH', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + TOUCH.transformArguments('key'), + ['TOUCH', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['TOUCH', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + TOUCH.transformArguments(['1', '2']), + ['TOUCH', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.touch', async client => { - assert.equal( - await client.touch('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('touch', async client => { + assert.equal( + await client.touch('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/TOUCH.ts b/packages/client/lib/commands/TOUCH.ts index e67dff8e932..c1c19402f8b 100644 --- a/packages/client/lib/commands/TOUCH.ts +++ b/packages/client/lib/commands/TOUCH.ts @@ -1,12 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['TOUCH'], key); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisVariadicArgument) { + return pushVariadicArguments(['TOUCH'], key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/TTL.spec.ts b/packages/client/lib/commands/TTL.spec.ts index e37a6ab714b..05c75851ee8 100644 --- a/packages/client/lib/commands/TTL.spec.ts +++ b/packages/client/lib/commands/TTL.spec.ts @@ -1,19 +1,23 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './TTL'; +import TTL from './TTL'; describe('TTL', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TTL', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + TTL.transformArguments('key'), + ['TTL', 'key'] + ); + }); - testUtils.testWithClient('client.ttl', async client => { - assert.equal( - await client.ttl('key'), - -2 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('ttl', async client => { + console.log(await client.get('key'), await client.ttl('key')); + assert.equal( + await client.ttl('key'), + -2 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/TTL.ts b/packages/client/lib/commands/TTL.ts index 29586f31fa8..65c3b7b026f 100644 --- a/packages/client/lib/commands/TTL.ts +++ b/packages/client/lib/commands/TTL.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['TTL', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/TYPE.spec.ts b/packages/client/lib/commands/TYPE.spec.ts index 1040bf979b3..01beef42e1c 100644 --- a/packages/client/lib/commands/TYPE.spec.ts +++ b/packages/client/lib/commands/TYPE.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './TYPE'; +import TYPE from './TYPE'; describe('TYPE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TYPE', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + TYPE.transformArguments('key'), + ['TYPE', 'key'] + ); + }); - testUtils.testWithClient('client.type', async client => { - assert.equal( - await client.type('key'), - 'none' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('type', async client => { + assert.equal( + await client.type('key'), + 'none' + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/TYPE.ts b/packages/client/lib/commands/TYPE.ts index 10cd3f99b0e..09f6887492c 100644 --- a/packages/client/lib/commands/TYPE.ts +++ b/packages/client/lib/commands/TYPE.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['TYPE', key]; -} - -export declare function transformReply(): RedisCommandArgument; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/UNLINK.spec.ts b/packages/client/lib/commands/UNLINK.spec.ts index e8355407d8f..726c05223a3 100644 --- a/packages/client/lib/commands/UNLINK.spec.ts +++ b/packages/client/lib/commands/UNLINK.spec.ts @@ -1,28 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './UNLINK'; +import UNLINK from './UNLINK'; describe('UNLINK', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['UNLINK', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + UNLINK.transformArguments('key'), + ['UNLINK', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['UNLINK', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + UNLINK.transformArguments(['1', '2']), + ['UNLINK', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.unlink', async client => { - assert.equal( - await client.unlink('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('unlink', async client => { + assert.equal( + await client.unlink('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/UNLINK.ts b/packages/client/lib/commands/UNLINK.ts index 53b0360e2df..2346573f397 100644 --- a/packages/client/lib/commands/UNLINK.ts +++ b/packages/client/lib/commands/UNLINK.ts @@ -1,12 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['UNLINK'], key); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments(key: RedisVariadicArgument) { + return pushVariadicArguments(['UNLINK'], key); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/UNWATCH.spec.ts b/packages/client/lib/commands/UNWATCH.spec.ts index 109ed0fa7c0..75bac71d853 100644 --- a/packages/client/lib/commands/UNWATCH.spec.ts +++ b/packages/client/lib/commands/UNWATCH.spec.ts @@ -1,19 +1,19 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './UNWATCH'; +import UNWATCH from './UNWATCH'; describe('UNWATCH', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['UNWATCH'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + UNWATCH.transformArguments(), + ['UNWATCH'] + ); + }); - testUtils.testWithClient('client.unwatch', async client => { - assert.equal( - await client.unwatch(), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.unwatch', async client => { + assert.equal( + await client.unwatch(), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/UNWATCH.ts b/packages/client/lib/commands/UNWATCH.ts index ce42e7697bf..9dd83b9aee7 100644 --- a/packages/client/lib/commands/UNWATCH.ts +++ b/packages/client/lib/commands/UNWATCH.ts @@ -1,5 +1,10 @@ -export function transformArguments(): Array { - return ['UNWATCH']; -} +import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments() { + return ['UNWATCH']; + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/WAIT.spec.ts b/packages/client/lib/commands/WAIT.spec.ts index c85ef598612..8a8fb3a36c4 100644 --- a/packages/client/lib/commands/WAIT.spec.ts +++ b/packages/client/lib/commands/WAIT.spec.ts @@ -1,19 +1,19 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './WAIT'; +import WAIT from './WAIT'; describe('WAIT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments(0, 1), - ['WAIT', '0', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + WAIT.transformArguments(0, 1), + ['WAIT', '0', '1'] + ); + }); - testUtils.testWithClient('client.wait', async client => { - assert.equal( - await client.wait(0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.wait', async client => { + assert.equal( + await client.wait(0, 1), + 0 + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/WAIT.ts b/packages/client/lib/commands/WAIT.ts index dff51ed9680..5c4bd156a62 100644 --- a/packages/client/lib/commands/WAIT.ts +++ b/packages/client/lib/commands/WAIT.ts @@ -1,7 +1,10 @@ -export const FIRST_KEY_INDEX = 1; +import { RedisArgument, SimpleStringReply, Command, NumberReply } from '../RESP/types'; -export function transformArguments(numberOfReplicas: number, timeout: number): Array { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(numberOfReplicas: number, timeout: number) { return ['WAIT', numberOfReplicas.toString(), timeout.toString()]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/WATCH.spec.ts b/packages/client/lib/commands/WATCH.spec.ts index acaa062874f..d533a0b3e13 100644 --- a/packages/client/lib/commands/WATCH.spec.ts +++ b/packages/client/lib/commands/WATCH.spec.ts @@ -1,20 +1,20 @@ import { strict as assert } from 'assert'; -import { transformArguments } from './WATCH'; +import WATCH from './WATCH'; describe('WATCH', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['WATCH', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + WATCH.transformArguments('key'), + ['WATCH', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['WATCH', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + WATCH.transformArguments(['1', '2']), + ['WATCH', '1', '2'] + ); }); + }); }); diff --git a/packages/client/lib/commands/WATCH.ts b/packages/client/lib/commands/WATCH.ts index 58c6dfd1dad..339642bf991 100644 --- a/packages/client/lib/commands/WATCH.ts +++ b/packages/client/lib/commands/WATCH.ts @@ -1,10 +1,11 @@ -import { RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { SimpleStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments(key: string | Array): RedisCommandArguments { - return pushVerdictArguments(['WATCH'], key); -} - -export declare function transformReply(): string; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(key: RedisVariadicArgument) { + return pushVariadicArguments(['WATCH'], key); + }, + transformReply: undefined as unknown as () => SimpleStringReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XACK.spec.ts b/packages/client/lib/commands/XACK.spec.ts index 0586a5921fd..fdfaee5fb59 100644 --- a/packages/client/lib/commands/XACK.spec.ts +++ b/packages/client/lib/commands/XACK.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XACK'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XACK'; -describe('XACK', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'group', '1-0'), - ['XACK', 'key', 'group', '1-0'] - ); - }); +// describe('XACK', () => { +// describe('transformArguments', () => { +// it('string', () => { +// assert.deepEqual( +// transformArguments('key', 'group', '1-0'), +// ['XACK', 'key', 'group', '1-0'] +// ); +// }); - it('array', () => { - assert.deepEqual( - transformArguments('key', 'group', ['1-0', '2-0']), - ['XACK', 'key', 'group', '1-0', '2-0'] - ); - }); - }); +// it('array', () => { +// assert.deepEqual( +// transformArguments('key', 'group', ['1-0', '2-0']), +// ['XACK', 'key', 'group', '1-0', '2-0'] +// ); +// }); +// }); - testUtils.testWithClient('client.xAck', async client => { - assert.equal( - await client.xAck('key', 'group', '1-0'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.xAck', async client => { +// assert.equal( +// await client.xAck('key', 'group', '1-0'), +// 0 +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XACK.ts b/packages/client/lib/commands/XACK.ts index 670d810fc00..4d54df00d1e 100644 --- a/packages/client/lib/commands/XACK.ts +++ b/packages/client/lib/commands/XACK.ts @@ -1,14 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - id: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['XACK', key, group], id); -} +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument, +// id: RedisCommandArgument | Array +// ): RedisCommandArguments { +// return pushVariadicArguments(['XACK', key, group], id); +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/XADD.spec.ts b/packages/client/lib/commands/XADD.spec.ts index 4b556ecc27c..c2b9c4f7bbe 100644 --- a/packages/client/lib/commands/XADD.spec.ts +++ b/packages/client/lib/commands/XADD.spec.ts @@ -1,118 +1,118 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XADD'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XADD'; -describe('XADD', () => { - describe('transformArguments', () => { - it('single field', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }), - ['XADD', 'key', '*', 'field', 'value'] - ); - }); +// describe('XADD', () => { +// describe('transformArguments', () => { +// it('single field', () => { +// assert.deepEqual( +// transformArguments('key', '*', { +// field: 'value' +// }), +// ['XADD', 'key', '*', 'field', 'value'] +// ); +// }); - it('multiple fields', () => { - assert.deepEqual( - transformArguments('key', '*', { - '1': 'I', - '2': 'II' - }), - ['XADD', 'key', '*', '1', 'I', '2', 'II'] - ); - }); +// it('multiple fields', () => { +// assert.deepEqual( +// transformArguments('key', '*', { +// '1': 'I', +// '2': 'II' +// }), +// ['XADD', 'key', '*', '1', 'I', '2', 'II'] +// ); +// }); - it('with NOMKSTREAM', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - NOMKSTREAM: true - }), - ['XADD', 'key', 'NOMKSTREAM', '*', 'field', 'value'] - ); - }); +// it('with NOMKSTREAM', () => { +// assert.deepEqual( +// transformArguments('key', '*', { +// field: 'value' +// }, { +// NOMKSTREAM: true +// }), +// ['XADD', 'key', 'NOMKSTREAM', '*', 'field', 'value'] +// ); +// }); - it('with TRIM', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - TRIM: { - threshold: 1000 - } - }), - ['XADD', 'key', '1000', '*', 'field', 'value'] - ); - }); +// it('with TRIM', () => { +// assert.deepEqual( +// transformArguments('key', '*', { +// field: 'value' +// }, { +// TRIM: { +// threshold: 1000 +// } +// }), +// ['XADD', 'key', '1000', '*', 'field', 'value'] +// ); +// }); - it('with TRIM.strategy', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - TRIM: { - strategy: 'MAXLEN', - threshold: 1000 - } - }), - ['XADD', 'key', 'MAXLEN', '1000', '*','field', 'value'] - ); - }); +// it('with TRIM.strategy', () => { +// assert.deepEqual( +// transformArguments('key', '*', { +// field: 'value' +// }, { +// TRIM: { +// strategy: 'MAXLEN', +// threshold: 1000 +// } +// }), +// ['XADD', 'key', 'MAXLEN', '1000', '*','field', 'value'] +// ); +// }); - it('with TRIM.strategyModifier', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - TRIM: { - strategyModifier: '=', - threshold: 1000 - } - }), - ['XADD', 'key', '=', '1000', '*', 'field', 'value'] - ); - }); +// it('with TRIM.strategyModifier', () => { +// assert.deepEqual( +// transformArguments('key', '*', { +// field: 'value' +// }, { +// TRIM: { +// strategyModifier: '=', +// threshold: 1000 +// } +// }), +// ['XADD', 'key', '=', '1000', '*', 'field', 'value'] +// ); +// }); - it('with TRIM.limit', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - TRIM: { - threshold: 1000, - limit: 1 - } - }), - ['XADD', 'key', '1000', 'LIMIT', '1', '*', 'field', 'value'] - ); - }); +// it('with TRIM.limit', () => { +// assert.deepEqual( +// transformArguments('key', '*', { +// field: 'value' +// }, { +// TRIM: { +// threshold: 1000, +// limit: 1 +// } +// }), +// ['XADD', 'key', '1000', 'LIMIT', '1', '*', 'field', 'value'] +// ); +// }); - it('with NOMKSTREAM, TRIM, TRIM.*', () => { - assert.deepEqual( - transformArguments('key', '*', { - field: 'value' - }, { - NOMKSTREAM: true, - TRIM: { - strategy: 'MAXLEN', - strategyModifier: '=', - threshold: 1000, - limit: 1 - } - }), - ['XADD', 'key', 'NOMKSTREAM', 'MAXLEN', '=', '1000', 'LIMIT', '1', '*', 'field', 'value'] - ); - }); - }); +// it('with NOMKSTREAM, TRIM, TRIM.*', () => { +// assert.deepEqual( +// transformArguments('key', '*', { +// field: 'value' +// }, { +// NOMKSTREAM: true, +// TRIM: { +// strategy: 'MAXLEN', +// strategyModifier: '=', +// threshold: 1000, +// limit: 1 +// } +// }), +// ['XADD', 'key', 'NOMKSTREAM', 'MAXLEN', '=', '1000', 'LIMIT', '1', '*', 'field', 'value'] +// ); +// }); +// }); - testUtils.testWithClient('client.xAdd', async client => { - assert.equal( - typeof await client.xAdd('key', '*', { - field: 'value' - }), - 'string' - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.xAdd', async client => { +// assert.equal( +// typeof await client.xAdd('key', '*', { +// field: 'value' +// }), +// 'string' +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XADD.ts b/packages/client/lib/commands/XADD.ts index e7a1b6804ff..16d7286ca70 100644 --- a/packages/client/lib/commands/XADD.ts +++ b/packages/client/lib/commands/XADD.ts @@ -1,52 +1,52 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; - -interface XAddOptions { - NOMKSTREAM?: true; - TRIM?: { - strategy?: 'MAXLEN' | 'MINID'; - strategyModifier?: '=' | '~'; - threshold: number; - limit?: number; - }; -} - -export function transformArguments( - key: RedisCommandArgument, - id: RedisCommandArgument, - message: Record, - options?: XAddOptions -): RedisCommandArguments { - const args = ['XADD', key]; - - if (options?.NOMKSTREAM) { - args.push('NOMKSTREAM'); - } - - if (options?.TRIM) { - if (options.TRIM.strategy) { - args.push(options.TRIM.strategy); - } - - if (options.TRIM.strategyModifier) { - args.push(options.TRIM.strategyModifier); - } - - args.push(options.TRIM.threshold.toString()); - - if (options.TRIM.limit) { - args.push('LIMIT', options.TRIM.limit.toString()); - } - } - - args.push(id); - - for (const [key, value] of Object.entries(message)) { - args.push(key, value); - } - - return args; -} - -export declare function transformReply(): string; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; + +// export const FIRST_KEY_INDEX = 1; + +// interface XAddOptions { +// NOMKSTREAM?: true; +// TRIM?: { +// strategy?: 'MAXLEN' | 'MINID'; +// strategyModifier?: '=' | '~'; +// threshold: number; +// limit?: number; +// }; +// } + +// export function transformArguments( +// key: RedisCommandArgument, +// id: RedisCommandArgument, +// message: Record, +// options?: XAddOptions +// ): RedisCommandArguments { +// const args = ['XADD', key]; + +// if (options?.NOMKSTREAM) { +// args.push('NOMKSTREAM'); +// } + +// if (options?.TRIM) { +// if (options.TRIM.strategy) { +// args.push(options.TRIM.strategy); +// } + +// if (options.TRIM.strategyModifier) { +// args.push(options.TRIM.strategyModifier); +// } + +// args.push(options.TRIM.threshold.toString()); + +// if (options.TRIM.limit) { +// args.push('LIMIT', options.TRIM.limit.toString()); +// } +// } + +// args.push(id); + +// for (const [key, value] of Object.entries(message)) { +// args.push(key, value); +// } + +// return args; +// } + +// export declare function transformReply(): string; diff --git a/packages/client/lib/commands/XAUTOCLAIM.spec.ts b/packages/client/lib/commands/XAUTOCLAIM.spec.ts index 4447a06d773..80382db8d22 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.spec.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.spec.ts @@ -1,42 +1,42 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XAUTOCLAIM'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XAUTOCLAIM'; -describe('XAUTOCLAIM', () => { - testUtils.isVersionGreaterThanHook([6, 2]); +// describe('XAUTOCLAIM', () => { +// testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0'), - ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0'] - ); - }); +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0'), +// ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0'] +// ); +// }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - COUNT: 1 - }), - ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'COUNT', '1'] - ); - }); - }); +// it('with COUNT', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0', { +// COUNT: 1 +// }), +// ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'COUNT', '1'] +// ); +// }); +// }); - testUtils.testWithClient('client.xAutoClaim', async client => { - await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xGroupCreateConsumer('key', 'group', 'consumer'), - ]); +// testUtils.testWithClient('client.xAutoClaim', async client => { +// await Promise.all([ +// client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }), +// client.xGroupCreateConsumer('key', 'group', 'consumer'), +// ]); - assert.deepEqual( - await client.xAutoClaim('key', 'group', 'consumer', 1, '0-0'), - { - nextId: '0-0', - messages: [] - } - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.deepEqual( +// await client.xAutoClaim('key', 'group', 'consumer', 1, '0-0'), +// { +// nextId: '0-0', +// messages: [] +// } +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XAUTOCLAIM.ts b/packages/client/lib/commands/XAUTOCLAIM.ts index 4bf46057bac..c32c3baf8d9 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.ts @@ -1,39 +1,39 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { StreamMessagesReply, transformStreamMessagesReply } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export interface XAutoClaimOptions { - COUNT?: number; -} - -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - consumer: RedisCommandArgument, - minIdleTime: number, - start: string, - options?: XAutoClaimOptions -): RedisCommandArguments { - const args = ['XAUTOCLAIM', key, group, consumer, minIdleTime.toString(), start]; - - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } - - return args; -} - -type XAutoClaimRawReply = [RedisCommandArgument, Array]; - -interface XAutoClaimReply { - nextId: RedisCommandArgument; - messages: StreamMessagesReply; -} - -export function transformReply(reply: XAutoClaimRawReply): XAutoClaimReply { - return { - nextId: reply[0], - messages: transformStreamMessagesReply(reply[1]) - }; -} +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { StreamMessagesReply, transformStreamMessagesReply } from './generic-transformers'; + +// export const FIRST_KEY_INDEX = 1; + +// export interface XAutoClaimOptions { +// COUNT?: number; +// } + +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument, +// consumer: RedisCommandArgument, +// minIdleTime: number, +// start: string, +// options?: XAutoClaimOptions +// ): RedisCommandArguments { +// const args = ['XAUTOCLAIM', key, group, consumer, minIdleTime.toString(), start]; + +// if (options?.COUNT) { +// args.push('COUNT', options.COUNT.toString()); +// } + +// return args; +// } + +// type XAutoClaimRawReply = [RedisCommandArgument, Array]; + +// interface XAutoClaimReply { +// nextId: RedisCommandArgument; +// messages: StreamMessagesReply; +// } + +// export function transformReply(reply: XAutoClaimRawReply): XAutoClaimReply { +// return { +// nextId: reply[0], +// messages: transformStreamMessagesReply(reply[1]) +// }; +// } diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts index 9aa24cd04a4..ed7b80cabfc 100644 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.spec.ts @@ -1,31 +1,31 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XAUTOCLAIM_JUSTID'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XAUTOCLAIM_JUSTID'; -describe('XAUTOCLAIM JUSTID', () => { - testUtils.isVersionGreaterThanHook([6, 2]); +// describe('XAUTOCLAIM JUSTID', () => { +// testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0'), - ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] - ); - }); +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0'), +// ['XAUTOCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] +// ); +// }); - testUtils.testWithClient('client.xAutoClaimJustId', async client => { - await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xGroupCreateConsumer('key', 'group', 'consumer'), - ]); +// testUtils.testWithClient('client.xAutoClaimJustId', async client => { +// await Promise.all([ +// client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }), +// client.xGroupCreateConsumer('key', 'group', 'consumer'), +// ]); - assert.deepEqual( - await client.xAutoClaimJustId('key', 'group', 'consumer', 1, '0-0'), - { - nextId: '0-0', - messages: [] - } - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.deepEqual( +// await client.xAutoClaimJustId('key', 'group', 'consumer', 1, '0-0'), +// { +// nextId: '0-0', +// messages: [] +// } +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts index a30ac1579e7..3f6374d9850 100644 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts @@ -1,25 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformXAutoClaimArguments } from './XAUTOCLAIM'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { transformArguments as transformXAutoClaimArguments } from './XAUTOCLAIM'; -export { FIRST_KEY_INDEX } from './XAUTOCLAIM'; +// export { FIRST_KEY_INDEX } from './XAUTOCLAIM'; -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformXAutoClaimArguments(...args), - 'JUSTID' - ]; -} +// export function transformArguments(...args: Parameters): RedisCommandArguments { +// return [ +// ...transformXAutoClaimArguments(...args), +// 'JUSTID' +// ]; +// } -type XAutoClaimJustIdRawReply = [RedisCommandArgument, Array]; +// type XAutoClaimJustIdRawReply = [RedisCommandArgument, Array]; -interface XAutoClaimJustIdReply { - nextId: RedisCommandArgument; - messages: Array; -} +// interface XAutoClaimJustIdReply { +// nextId: RedisCommandArgument; +// messages: Array; +// } -export function transformReply(reply: XAutoClaimJustIdRawReply): XAutoClaimJustIdReply { - return { - nextId: reply[0], - messages: reply[1] - }; -} +// export function transformReply(reply: XAutoClaimJustIdRawReply): XAutoClaimJustIdReply { +// return { +// nextId: reply[0], +// messages: reply[1] +// }; +// } diff --git a/packages/client/lib/commands/XCLAIM.spec.ts b/packages/client/lib/commands/XCLAIM.spec.ts index 141a62ab77a..a9f6879cfa1 100644 --- a/packages/client/lib/commands/XCLAIM.spec.ts +++ b/packages/client/lib/commands/XCLAIM.spec.ts @@ -1,90 +1,90 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XCLAIM'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XCLAIM'; -describe('XCLAIM', () => { - describe('transformArguments', () => { - it('single id (string)', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0'), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0'] - ); - }); +// describe('XCLAIM', () => { +// describe('transformArguments', () => { +// it('single id (string)', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0'), +// ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0'] +// ); +// }); - it('multiple ids (array)', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, ['0-0', '1-0']), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', '1-0'] - ); - }); +// it('multiple ids (array)', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, ['0-0', '1-0']), +// ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', '1-0'] +// ); +// }); - it('with IDLE', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - IDLE: 1 - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1'] - ); - }); +// it('with IDLE', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0', { +// IDLE: 1 +// }), +// ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1'] +// ); +// }); - it('with TIME (number)', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - TIME: 1 - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', '1'] - ); - }); +// it('with TIME (number)', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0', { +// TIME: 1 +// }), +// ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', '1'] +// ); +// }); - it('with TIME (date)', () => { - const d = new Date(); - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - TIME: d - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', d.getTime().toString()] - ); - }); +// it('with TIME (date)', () => { +// const d = new Date(); +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0', { +// TIME: d +// }), +// ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'TIME', d.getTime().toString()] +// ); +// }); - it('with RETRYCOUNT', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - RETRYCOUNT: 1 - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'RETRYCOUNT', '1'] - ); - }); +// it('with RETRYCOUNT', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0', { +// RETRYCOUNT: 1 +// }), +// ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'RETRYCOUNT', '1'] +// ); +// }); - it('with FORCE', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - FORCE: true - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'FORCE'] - ); - }); +// it('with FORCE', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0', { +// FORCE: true +// }), +// ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'FORCE'] +// ); +// }); - it('with IDLE, TIME, RETRYCOUNT, FORCE, JUSTID', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0', { - IDLE: 1, - TIME: 1, - RETRYCOUNT: 1, - FORCE: true - }), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1', 'TIME', '1', 'RETRYCOUNT', '1', 'FORCE'] - ); - }); - }); +// it('with IDLE, TIME, RETRYCOUNT, FORCE, JUSTID', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0', { +// IDLE: 1, +// TIME: 1, +// RETRYCOUNT: 1, +// FORCE: true +// }), +// ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'IDLE', '1', 'TIME', '1', 'RETRYCOUNT', '1', 'FORCE'] +// ); +// }); +// }); - testUtils.testWithClient('client.xClaim', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// testUtils.testWithClient('client.xClaim', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.deepEqual( - await client.xClaim('key', 'group', 'consumer', 1, '0-0'), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.deepEqual( +// await client.xClaim('key', 'group', 'consumer', 1, '0-0'), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XCLAIM.ts b/packages/client/lib/commands/XCLAIM.ts index bc38f9b9e95..6c1328441aa 100644 --- a/packages/client/lib/commands/XCLAIM.ts +++ b/packages/client/lib/commands/XCLAIM.ts @@ -1,48 +1,48 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export interface XClaimOptions { - IDLE?: number; - TIME?: number | Date; - RETRYCOUNT?: number; - FORCE?: true; -} - -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - consumer: RedisCommandArgument, - minIdleTime: number, - id: RedisCommandArgument | Array, - options?: XClaimOptions -): RedisCommandArguments { - const args = pushVerdictArguments( - ['XCLAIM', key, group, consumer, minIdleTime.toString()], - id - ); - - if (options?.IDLE) { - args.push('IDLE', options.IDLE.toString()); - } - - if (options?.TIME) { - args.push( - 'TIME', - (typeof options.TIME === 'number' ? options.TIME : options.TIME.getTime()).toString() - ); - } - - if (options?.RETRYCOUNT) { - args.push('RETRYCOUNT', options.RETRYCOUNT.toString()); - } - - if (options?.FORCE) { - args.push('FORCE'); - } - - return args; -} - -export { transformStreamMessagesReply as transformReply } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { pushVariadicArguments } from './generic-transformers'; + +// export const FIRST_KEY_INDEX = 1; + +// export interface XClaimOptions { +// IDLE?: number; +// TIME?: number | Date; +// RETRYCOUNT?: number; +// FORCE?: true; +// } + +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument, +// consumer: RedisCommandArgument, +// minIdleTime: number, +// id: RedisCommandArgument | Array, +// options?: XClaimOptions +// ): RedisCommandArguments { +// const args = pushVariadicArguments( +// ['XCLAIM', key, group, consumer, minIdleTime.toString()], +// id +// ); + +// if (options?.IDLE) { +// args.push('IDLE', options.IDLE.toString()); +// } + +// if (options?.TIME) { +// args.push( +// 'TIME', +// (typeof options.TIME === 'number' ? options.TIME : options.TIME.getTime()).toString() +// ); +// } + +// if (options?.RETRYCOUNT) { +// args.push('RETRYCOUNT', options.RETRYCOUNT.toString()); +// } + +// if (options?.FORCE) { +// args.push('FORCE'); +// } + +// return args; +// } + +// export { transformStreamMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts b/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts index 619f876d53d..88fe59084b5 100644 --- a/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts +++ b/packages/client/lib/commands/XCLAIM_JUSTID.spec.ts @@ -1,23 +1,23 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XCLAIM_JUSTID'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XCLAIM_JUSTID'; -describe('XCLAIM JUSTID', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer', 1, '0-0'), - ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] - ); - }); +// describe('XCLAIM JUSTID', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer', 1, '0-0'), +// ['XCLAIM', 'key', 'group', 'consumer', '1', '0-0', 'JUSTID'] +// ); +// }); - testUtils.testWithClient('client.xClaimJustId', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// testUtils.testWithClient('client.xClaimJustId', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.deepEqual( - await client.xClaimJustId('key', 'group', 'consumer', 1, '0-0'), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.deepEqual( +// await client.xClaimJustId('key', 'group', 'consumer', 1, '0-0'), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XCLAIM_JUSTID.ts b/packages/client/lib/commands/XCLAIM_JUSTID.ts index 50d0d5a0366..7c011e692fc 100644 --- a/packages/client/lib/commands/XCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XCLAIM_JUSTID.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformXClaimArguments } from './XCLAIM'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { transformArguments as transformXClaimArguments } from './XCLAIM'; -export { FIRST_KEY_INDEX } from './XCLAIM'; +// export { FIRST_KEY_INDEX } from './XCLAIM'; -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformXClaimArguments(...args), - 'JUSTID' - ]; -} +// export function transformArguments(...args: Parameters): RedisCommandArguments { +// return [ +// ...transformXClaimArguments(...args), +// 'JUSTID' +// ]; +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/XDEL.spec.ts b/packages/client/lib/commands/XDEL.spec.ts index 00f9e2f9c67..4f7d48aa0ee 100644 --- a/packages/client/lib/commands/XDEL.spec.ts +++ b/packages/client/lib/commands/XDEL.spec.ts @@ -1,28 +1,28 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XDEL'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XDEL'; -describe('XDEL', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', '0-0'), - ['XDEL', 'key', '0-0'] - ); - }); +// describe('XDEL', () => { +// describe('transformArguments', () => { +// it('string', () => { +// assert.deepEqual( +// transformArguments('key', '0-0'), +// ['XDEL', 'key', '0-0'] +// ); +// }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['0-0', '1-0']), - ['XDEL', 'key', '0-0', '1-0'] - ); - }); - }); +// it('array', () => { +// assert.deepEqual( +// transformArguments('key', ['0-0', '1-0']), +// ['XDEL', 'key', '0-0', '1-0'] +// ); +// }); +// }); - testUtils.testWithClient('client.xDel', async client => { - assert.equal( - await client.xDel('key', '0-0'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.xDel', async client => { +// assert.equal( +// await client.xDel('key', '0-0'), +// 0 +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XDEL.ts b/packages/client/lib/commands/XDEL.ts index 82b30d21092..dab033fe572 100644 --- a/packages/client/lib/commands/XDEL.ts +++ b/packages/client/lib/commands/XDEL.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments( - key: RedisCommandArgument, - id: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['XDEL', key], id); -} +// export function transformArguments( +// key: RedisCommandArgument, +// id: RedisCommandArgument | Array +// ): RedisCommandArguments { +// return pushVariadicArguments(['XDEL', key], id); +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/XGROUP_CREATE.spec.ts b/packages/client/lib/commands/XGROUP_CREATE.spec.ts index 57516e44cc8..831db73e682 100644 --- a/packages/client/lib/commands/XGROUP_CREATE.spec.ts +++ b/packages/client/lib/commands/XGROUP_CREATE.spec.ts @@ -1,32 +1,32 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XGROUP_CREATE'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XGROUP_CREATE'; -describe('XGROUP CREATE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'group', '$'), - ['XGROUP', 'CREATE', 'key', 'group', '$'] - ); - }); +// describe('XGROUP CREATE', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('key', 'group', '$'), +// ['XGROUP', 'CREATE', 'key', 'group', '$'] +// ); +// }); - it('with MKSTREAM', () => { - assert.deepEqual( - transformArguments('key', 'group', '$', { - MKSTREAM: true - }), - ['XGROUP', 'CREATE', 'key', 'group', '$', 'MKSTREAM'] - ); - }); - }); +// it('with MKSTREAM', () => { +// assert.deepEqual( +// transformArguments('key', 'group', '$', { +// MKSTREAM: true +// }), +// ['XGROUP', 'CREATE', 'key', 'group', '$', 'MKSTREAM'] +// ); +// }); +// }); - testUtils.testWithClient('client.xGroupCreate', async client => { - assert.equal( - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.xGroupCreate', async client => { +// assert.equal( +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }), +// 'OK' +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XGROUP_CREATE.ts b/packages/client/lib/commands/XGROUP_CREATE.ts index 8cfd4e262e4..def2bf39eb3 100644 --- a/packages/client/lib/commands/XGROUP_CREATE.ts +++ b/packages/client/lib/commands/XGROUP_CREATE.ts @@ -1,24 +1,24 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 2; +// export const FIRST_KEY_INDEX = 2; -interface XGroupCreateOptions { - MKSTREAM?: true; -} +// interface XGroupCreateOptions { +// MKSTREAM?: true; +// } -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - id: RedisCommandArgument, - options?: XGroupCreateOptions -): RedisCommandArguments { - const args = ['XGROUP', 'CREATE', key, group, id]; +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument, +// id: RedisCommandArgument, +// options?: XGroupCreateOptions +// ): RedisCommandArguments { +// const args = ['XGROUP', 'CREATE', key, group, id]; - if (options?.MKSTREAM) { - args.push('MKSTREAM'); - } +// if (options?.MKSTREAM) { +// args.push('MKSTREAM'); +// } - return args; -} +// return args; +// } -export declare function transformReply(): RedisCommandArgument; +// export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts index 62443345188..1338b8837a6 100644 --- a/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.spec.ts @@ -1,25 +1,25 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XGROUP_CREATECONSUMER'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XGROUP_CREATECONSUMER'; -describe('XGROUP CREATECONSUMER', () => { - testUtils.isVersionGreaterThanHook([6, 2]); +// describe('XGROUP CREATECONSUMER', () => { +// testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer'), - ['XGROUP', 'CREATECONSUMER', 'key', 'group', 'consumer'] - ); - }); +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer'), +// ['XGROUP', 'CREATECONSUMER', 'key', 'group', 'consumer'] +// ); +// }); - testUtils.testWithClient('client.xGroupCreateConsumer', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// testUtils.testWithClient('client.xGroupCreateConsumer', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.equal( - await client.xGroupCreateConsumer('key', 'group', 'consumer'), - true - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.equal( +// await client.xGroupCreateConsumer('key', 'group', 'consumer'), +// true +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts index 2b816a6b480..8fb9ad2ad3a 100644 --- a/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_CREATECONSUMER.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 2; +// export const FIRST_KEY_INDEX = 2; -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - consumer: RedisCommandArgument -): RedisCommandArguments { - return ['XGROUP', 'CREATECONSUMER', key, group, consumer]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument, +// consumer: RedisCommandArgument +// ): RedisCommandArguments { +// return ['XGROUP', 'CREATECONSUMER', key, group, consumer]; +// } -export { transformBooleanReply as transformReply } from './generic-transformers'; +// export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts index d071aedf64f..c1704b56192 100644 --- a/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts +++ b/packages/client/lib/commands/XGROUP_DELCONSUMER.spec.ts @@ -1,23 +1,23 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XGROUP_DELCONSUMER'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XGROUP_DELCONSUMER'; -describe('XGROUP DELCONSUMER', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group', 'consumer'), - ['XGROUP', 'DELCONSUMER', 'key', 'group', 'consumer'] - ); - }); +// describe('XGROUP DELCONSUMER', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 'group', 'consumer'), +// ['XGROUP', 'DELCONSUMER', 'key', 'group', 'consumer'] +// ); +// }); - testUtils.testWithClient('client.xGroupDelConsumer', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// testUtils.testWithClient('client.xGroupDelConsumer', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.equal( - await client.xGroupDelConsumer('key', 'group', 'consumer'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.equal( +// await client.xGroupDelConsumer('key', 'group', 'consumer'), +// 0 +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XGROUP_DELCONSUMER.ts b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts index 4e4fc096d07..ad54beaf9dc 100644 --- a/packages/client/lib/commands/XGROUP_DELCONSUMER.ts +++ b/packages/client/lib/commands/XGROUP_DELCONSUMER.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 2; +// export const FIRST_KEY_INDEX = 2; -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - consumer: RedisCommandArgument -): RedisCommandArguments { - return ['XGROUP', 'DELCONSUMER', key, group, consumer]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument, +// consumer: RedisCommandArgument +// ): RedisCommandArguments { +// return ['XGROUP', 'DELCONSUMER', key, group, consumer]; +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/XGROUP_DESTROY.spec.ts b/packages/client/lib/commands/XGROUP_DESTROY.spec.ts index ea8e7b7be98..3e729337aee 100644 --- a/packages/client/lib/commands/XGROUP_DESTROY.spec.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.spec.ts @@ -1,23 +1,23 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XGROUP_DESTROY'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XGROUP_DESTROY'; -describe('XGROUP DESTROY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group'), - ['XGROUP', 'DESTROY', 'key', 'group'] - ); - }); +// describe('XGROUP DESTROY', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 'group'), +// ['XGROUP', 'DESTROY', 'key', 'group'] +// ); +// }); - testUtils.testWithClient('client.xGroupDestroy', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// testUtils.testWithClient('client.xGroupDestroy', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.equal( - await client.xGroupDestroy('key', 'group'), - true - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.equal( +// await client.xGroupDestroy('key', 'group'), +// true +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XGROUP_DESTROY.ts b/packages/client/lib/commands/XGROUP_DESTROY.ts index 85910c02471..077afba4dc7 100644 --- a/packages/client/lib/commands/XGROUP_DESTROY.ts +++ b/packages/client/lib/commands/XGROUP_DESTROY.ts @@ -1,12 +1,12 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 2; +// export const FIRST_KEY_INDEX = 2; -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument -): RedisCommandArguments { - return ['XGROUP', 'DESTROY', key, group]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument +// ): RedisCommandArguments { +// return ['XGROUP', 'DESTROY', key, group]; +// } -export { transformBooleanReply as transformReply } from './generic-transformers'; +// export { transformBooleanReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XGROUP_SETID.spec.ts b/packages/client/lib/commands/XGROUP_SETID.spec.ts index 8df51f5401d..6f05802ab00 100644 --- a/packages/client/lib/commands/XGROUP_SETID.spec.ts +++ b/packages/client/lib/commands/XGROUP_SETID.spec.ts @@ -1,23 +1,23 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XGROUP_SETID'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XGROUP_SETID'; -describe('XGROUP SETID', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group', '0'), - ['XGROUP', 'SETID', 'key', 'group', '0'] - ); - }); +// describe('XGROUP SETID', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 'group', '0'), +// ['XGROUP', 'SETID', 'key', 'group', '0'] +// ); +// }); - testUtils.testWithClient('client.xGroupSetId', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// testUtils.testWithClient('client.xGroupSetId', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.equal( - await client.xGroupSetId('key', 'group', '0'), - 'OK' - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.equal( +// await client.xGroupSetId('key', 'group', '0'), +// 'OK' +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XGROUP_SETID.ts b/packages/client/lib/commands/XGROUP_SETID.ts index e732fc8d7bf..65e1e257fb1 100644 --- a/packages/client/lib/commands/XGROUP_SETID.ts +++ b/packages/client/lib/commands/XGROUP_SETID.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 2; +// export const FIRST_KEY_INDEX = 2; -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - id: RedisCommandArgument -): RedisCommandArguments { - return ['XGROUP', 'SETID', key, group, id]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument, +// id: RedisCommandArgument +// ): RedisCommandArguments { +// return ['XGROUP', 'SETID', key, group, id]; +// } -export declare function transformReply(): RedisCommandArgument; +// export declare function transformReply(): RedisCommandArgument; diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts index 87c82b34f29..bfbd946bba5 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.spec.ts @@ -1,41 +1,41 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './XINFO_CONSUMERS'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments, transformReply } from './XINFO_CONSUMERS'; -describe('XINFO CONSUMERS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group'), - ['XINFO', 'CONSUMERS', 'key', 'group'] - ); - }); +// describe('XINFO CONSUMERS', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 'group'), +// ['XINFO', 'CONSUMERS', 'key', 'group'] +// ); +// }); - it('transformReply', () => { - assert.deepEqual( - transformReply([ - ['name', 'Alice', 'pending', 1, 'idle', 9104628], - ['name', 'Bob', 'pending', 1, 'idle', 83841983] - ]), - [{ - name: 'Alice', - pending: 1, - idle: 9104628 - }, { - name: 'Bob', - pending: 1, - idle: 83841983 - }] - ); - }); +// it('transformReply', () => { +// assert.deepEqual( +// transformReply([ +// ['name', 'Alice', 'pending', 1, 'idle', 9104628], +// ['name', 'Bob', 'pending', 1, 'idle', 83841983] +// ]), +// [{ +// name: 'Alice', +// pending: 1, +// idle: 9104628 +// }, { +// name: 'Bob', +// pending: 1, +// idle: 83841983 +// }] +// ); +// }); - testUtils.testWithClient('client.xInfoConsumers', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// testUtils.testWithClient('client.xInfoConsumers', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.deepEqual( - await client.xInfoConsumers('key', 'group'), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.deepEqual( +// await client.xInfoConsumers('key', 'group'), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.ts b/packages/client/lib/commands/XINFO_CONSUMERS.ts index 05e3a26b172..a9c5cb75e42 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.ts @@ -1,26 +1,26 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 2; +// export const FIRST_KEY_INDEX = 2; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument -): RedisCommandArguments { - return ['XINFO', 'CONSUMERS', key, group]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument +// ): RedisCommandArguments { +// return ['XINFO', 'CONSUMERS', key, group]; +// } -type XInfoConsumersReply = Array<{ - name: RedisCommandArgument; - pending: number; - idle: number; -}>; +// type XInfoConsumersReply = Array<{ +// name: RedisCommandArgument; +// pending: number; +// idle: number; +// }>; -export function transformReply(rawReply: Array): XInfoConsumersReply { - return rawReply.map(consumer => ({ - name: consumer[1], - pending: consumer[3], - idle: consumer[5] - })); -} +// export function transformReply(rawReply: Array): XInfoConsumersReply { +// return rawReply.map(consumer => ({ +// name: consumer[1], +// pending: consumer[3], +// idle: consumer[5] +// })); +// } diff --git a/packages/client/lib/commands/XINFO_GROUPS.spec.ts b/packages/client/lib/commands/XINFO_GROUPS.spec.ts index dea8ac58d9c..7380251db6d 100644 --- a/packages/client/lib/commands/XINFO_GROUPS.spec.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.spec.ts @@ -1,48 +1,48 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './XINFO_GROUPS'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments, transformReply } from './XINFO_GROUPS'; -describe('XINFO GROUPS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['XINFO', 'GROUPS', 'key'] - ); - }); +// describe('XINFO GROUPS', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key'), +// ['XINFO', 'GROUPS', 'key'] +// ); +// }); - it('transformReply', () => { - assert.deepEqual( - transformReply([ - ['name', 'mygroup', 'consumers', 2, 'pending', 2, 'last-delivered-id', '1588152489012-0'], - ['name', 'some-other-group', 'consumers', 1, 'pending', 0, 'last-delivered-id', '1588152498034-0'] - ]), - [{ - name: 'mygroup', - consumers: 2, - pending: 2, - lastDeliveredId: '1588152489012-0' - }, { - name: 'some-other-group', - consumers: 1, - pending: 0, - lastDeliveredId: '1588152498034-0' - }] - ); - }); +// it('transformReply', () => { +// assert.deepEqual( +// transformReply([ +// ['name', 'mygroup', 'consumers', 2, 'pending', 2, 'last-delivered-id', '1588152489012-0'], +// ['name', 'some-other-group', 'consumers', 1, 'pending', 0, 'last-delivered-id', '1588152498034-0'] +// ]), +// [{ +// name: 'mygroup', +// consumers: 2, +// pending: 2, +// lastDeliveredId: '1588152489012-0' +// }, { +// name: 'some-other-group', +// consumers: 1, +// pending: 0, +// lastDeliveredId: '1588152498034-0' +// }] +// ); +// }); - testUtils.testWithClient('client.xInfoGroups', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// testUtils.testWithClient('client.xInfoGroups', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.deepEqual( - await client.xInfoGroups('key'), - [{ - name: 'group', - consumers: 0, - pending: 0, - lastDeliveredId: '0-0' - }] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.deepEqual( +// await client.xInfoGroups('key'), +// [{ +// name: 'group', +// consumers: 0, +// pending: 0, +// lastDeliveredId: '0-0' +// }] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XINFO_GROUPS.ts b/packages/client/lib/commands/XINFO_GROUPS.ts index dcf504c8ce7..4dac9eed857 100644 --- a/packages/client/lib/commands/XINFO_GROUPS.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.ts @@ -1,25 +1,25 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 2; +// export const FIRST_KEY_INDEX = 2; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return ['XINFO', 'GROUPS', key]; -} +// export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +// return ['XINFO', 'GROUPS', key]; +// } -type XInfoGroupsReply = Array<{ - name: RedisCommandArgument; - consumers: number; - pending: number; - lastDeliveredId: RedisCommandArgument; -}>; +// type XInfoGroupsReply = Array<{ +// name: RedisCommandArgument; +// consumers: number; +// pending: number; +// lastDeliveredId: RedisCommandArgument; +// }>; -export function transformReply(rawReply: Array): XInfoGroupsReply { - return rawReply.map(group => ({ - name: group[1], - consumers: group[3], - pending: group[5], - lastDeliveredId: group[7] - })); -} +// export function transformReply(rawReply: Array): XInfoGroupsReply { +// return rawReply.map(group => ({ +// name: group[1], +// consumers: group[3], +// pending: group[5], +// lastDeliveredId: group[7] +// })); +// } diff --git a/packages/client/lib/commands/XINFO_STREAM.spec.ts b/packages/client/lib/commands/XINFO_STREAM.spec.ts index ca8d44f2875..a73c4607080 100644 --- a/packages/client/lib/commands/XINFO_STREAM.spec.ts +++ b/packages/client/lib/commands/XINFO_STREAM.spec.ts @@ -1,72 +1,72 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './XINFO_STREAM'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments, transformReply } from './XINFO_STREAM'; -describe('XINFO STREAM', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['XINFO', 'STREAM', 'key'] - ); - }); +// describe('XINFO STREAM', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key'), +// ['XINFO', 'STREAM', 'key'] +// ); +// }); - it('transformReply', () => { - assert.deepEqual( - transformReply([ - 'length', 2, - 'radix-tree-keys', 1, - 'radix-tree-nodes', 2, - 'last-generated-id', '1538385846314-0', - 'groups', 2, - 'first-entry', ['1538385820729-0', ['foo', 'bar']], - 'last-entry', ['1538385846314-0', ['field', 'value']] - ]), - { - length: 2, - radixTreeKeys: 1, - radixTreeNodes: 2, - groups: 2, - lastGeneratedId: '1538385846314-0', - firstEntry: { - id: '1538385820729-0', - message: Object.create(null, { - foo: { - value: 'bar', - configurable: true, - enumerable: true - } - }) - }, - lastEntry: { - id: '1538385846314-0', - message: Object.create(null, { - field: { - value: 'value', - configurable: true, - enumerable: true - } - }) - } - } - ); - }); +// it('transformReply', () => { +// assert.deepEqual( +// transformReply([ +// 'length', 2, +// 'radix-tree-keys', 1, +// 'radix-tree-nodes', 2, +// 'last-generated-id', '1538385846314-0', +// 'groups', 2, +// 'first-entry', ['1538385820729-0', ['foo', 'bar']], +// 'last-entry', ['1538385846314-0', ['field', 'value']] +// ]), +// { +// length: 2, +// radixTreeKeys: 1, +// radixTreeNodes: 2, +// groups: 2, +// lastGeneratedId: '1538385846314-0', +// firstEntry: { +// id: '1538385820729-0', +// message: Object.create(null, { +// foo: { +// value: 'bar', +// configurable: true, +// enumerable: true +// } +// }) +// }, +// lastEntry: { +// id: '1538385846314-0', +// message: Object.create(null, { +// field: { +// value: 'value', +// configurable: true, +// enumerable: true +// } +// }) +// } +// } +// ); +// }); - testUtils.testWithClient('client.xInfoStream', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// testUtils.testWithClient('client.xInfoStream', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.deepEqual( - await client.xInfoStream('key'), - { - length: 0, - radixTreeKeys: 0, - radixTreeNodes: 1, - groups: 1, - lastGeneratedId: '0-0', - firstEntry: null, - lastEntry: null - } - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.deepEqual( +// await client.xInfoStream('key'), +// { +// length: 0, +// radixTreeKeys: 0, +// radixTreeNodes: 1, +// groups: 1, +// lastGeneratedId: '0-0', +// firstEntry: null, +// lastEntry: null +// } +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XINFO_STREAM.ts b/packages/client/lib/commands/XINFO_STREAM.ts index e9de25be8cb..32fb83fe0a8 100644 --- a/packages/client/lib/commands/XINFO_STREAM.ts +++ b/packages/client/lib/commands/XINFO_STREAM.ts @@ -1,64 +1,64 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { StreamMessageReply, transformTuplesReply } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { StreamMessageReply, transformTuplesReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +// export const FIRST_KEY_INDEX = 2; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return ['XINFO', 'STREAM', key]; -} +// export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +// return ['XINFO', 'STREAM', key]; +// } -interface XInfoStreamReply { - length: number; - radixTreeKeys: number; - radixTreeNodes: number; - groups: number; - lastGeneratedId: RedisCommandArgument; - firstEntry: StreamMessageReply | null; - lastEntry: StreamMessageReply | null; -} +// interface XInfoStreamReply { +// length: number; +// radixTreeKeys: number; +// radixTreeNodes: number; +// groups: number; +// lastGeneratedId: RedisCommandArgument; +// firstEntry: StreamMessageReply | null; +// lastEntry: StreamMessageReply | null; +// } -export function transformReply(rawReply: Array): XInfoStreamReply { - const parsedReply: Partial = {}; +// export function transformReply(rawReply: Array): XInfoStreamReply { +// const parsedReply: Partial = {}; - for (let i = 0; i < rawReply.length; i+= 2) { - switch (rawReply[i]) { - case 'length': - parsedReply.length = rawReply[i + 1]; - break; +// for (let i = 0; i < rawReply.length; i+= 2) { +// switch (rawReply[i]) { +// case 'length': +// parsedReply.length = rawReply[i + 1]; +// break; - case 'radix-tree-keys': - parsedReply.radixTreeKeys = rawReply[i + 1]; - break; +// case 'radix-tree-keys': +// parsedReply.radixTreeKeys = rawReply[i + 1]; +// break; - case 'radix-tree-nodes': - parsedReply.radixTreeNodes = rawReply[i + 1]; - break; +// case 'radix-tree-nodes': +// parsedReply.radixTreeNodes = rawReply[i + 1]; +// break; - case 'groups': - parsedReply.groups = rawReply[i + 1]; - break; +// case 'groups': +// parsedReply.groups = rawReply[i + 1]; +// break; - case 'last-generated-id': - parsedReply.lastGeneratedId = rawReply[i + 1]; - break; +// case 'last-generated-id': +// parsedReply.lastGeneratedId = rawReply[i + 1]; +// break; - case 'first-entry': - parsedReply.firstEntry = rawReply[i + 1] ? { - id: rawReply[i + 1][0], - message: transformTuplesReply(rawReply[i + 1][1]) - } : null; - break; +// case 'first-entry': +// parsedReply.firstEntry = rawReply[i + 1] ? { +// id: rawReply[i + 1][0], +// message: transformTuplesReply(rawReply[i + 1][1]) +// } : null; +// break; - case 'last-entry': - parsedReply.lastEntry = rawReply[i + 1] ? { - id: rawReply[i + 1][0], - message: transformTuplesReply(rawReply[i + 1][1]) - } : null; - break; - } - } +// case 'last-entry': +// parsedReply.lastEntry = rawReply[i + 1] ? { +// id: rawReply[i + 1][0], +// message: transformTuplesReply(rawReply[i + 1][1]) +// } : null; +// break; +// } +// } - return parsedReply as XInfoStreamReply; -} +// return parsedReply as XInfoStreamReply; +// } diff --git a/packages/client/lib/commands/XLEN.spec.ts b/packages/client/lib/commands/XLEN.spec.ts index 178024ba89e..8f2bf1a8c77 100644 --- a/packages/client/lib/commands/XLEN.spec.ts +++ b/packages/client/lib/commands/XLEN.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XLEN'; +import XLEN from './XLEN'; describe('XLEN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['XLEN', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + XLEN.transformArguments('key'), + ['XLEN', 'key'] + ); + }); - testUtils.testWithClient('client.xLen', async client => { - assert.equal( - await client.xLen('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('xLen', async client => { + assert.equal( + await client.xLen('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/XLEN.ts b/packages/client/lib/commands/XLEN.ts index fda4192c8a0..d2ed566a190 100644 --- a/packages/client/lib/commands/XLEN.ts +++ b/packages/client/lib/commands/XLEN.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['XLEN', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/XPENDING.spec.ts b/packages/client/lib/commands/XPENDING.spec.ts index b1fef2a217f..bfa36d01e34 100644 --- a/packages/client/lib/commands/XPENDING.spec.ts +++ b/packages/client/lib/commands/XPENDING.spec.ts @@ -1,62 +1,62 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XPENDING'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XPENDING'; -describe('XPENDING', () => { - describe('transformArguments', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'group'), - ['XPENDING', 'key', 'group'] - ); - }); - }); +// describe('XPENDING', () => { +// describe('transformArguments', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 'group'), +// ['XPENDING', 'key', 'group'] +// ); +// }); +// }); - describe('client.xPending', () => { - testUtils.testWithClient('simple', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// describe('client.xPending', () => { +// testUtils.testWithClient('simple', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.deepEqual( - await client.xPending('key', 'group'), - { - pending: 0, - firstId: null, - lastId: null, - consumers: null - } - ); - }, GLOBAL.SERVERS.OPEN); +// assert.deepEqual( +// await client.xPending('key', 'group'), +// { +// pending: 0, +// firstId: null, +// lastId: null, +// consumers: null +// } +// ); +// }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('with consumers', async client => { - const [,, id] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xGroupCreateConsumer('key', 'group', 'consumer'), - client.xAdd('key', '*', { field: 'value' }), - client.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); +// testUtils.testWithClient('with consumers', async client => { +// const [,, id] = await Promise.all([ +// client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }), +// client.xGroupCreateConsumer('key', 'group', 'consumer'), +// client.xAdd('key', '*', { field: 'value' }), +// client.xReadGroup('group', 'consumer', { +// key: 'key', +// id: '>' +// }) +// ]); - assert.deepEqual( - await client.xPending('key', 'group'), - { - pending: 1, - firstId: id, - lastId: id, - consumers: [{ - name: 'consumer', - deliveriesCounter: 1 - }] - } - ); - }, { - ...GLOBAL.SERVERS.OPEN, - minimumDockerVersion: [6, 2] - }); - }); -}); +// assert.deepEqual( +// await client.xPending('key', 'group'), +// { +// pending: 1, +// firstId: id, +// lastId: id, +// consumers: [{ +// name: 'consumer', +// deliveriesCounter: 1 +// }] +// } +// ); +// }, { +// ...GLOBAL.SERVERS.OPEN, +// minimumDockerVersion: [6, 2] +// }); +// }); +// }); diff --git a/packages/client/lib/commands/XPENDING.ts b/packages/client/lib/commands/XPENDING.ts index ac56e429410..215295afd25 100644 --- a/packages/client/lib/commands/XPENDING.ts +++ b/packages/client/lib/commands/XPENDING.ts @@ -1,44 +1,44 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument -): RedisCommandArguments { - return ['XPENDING', key, group]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument +// ): RedisCommandArguments { +// return ['XPENDING', key, group]; +// } -type XPendingRawReply = [ - pending: number, - firstId: RedisCommandArgument | null, - lastId: RedisCommandArgument | null, - consumers: Array<[ - name: RedisCommandArgument, - deliveriesCounter: RedisCommandArgument - ]> | null -]; +// type XPendingRawReply = [ +// pending: number, +// firstId: RedisCommandArgument | null, +// lastId: RedisCommandArgument | null, +// consumers: Array<[ +// name: RedisCommandArgument, +// deliveriesCounter: RedisCommandArgument +// ]> | null +// ]; -interface XPendingReply { - pending: number; - firstId: RedisCommandArgument | null; - lastId: RedisCommandArgument | null; - consumers: Array<{ - name: RedisCommandArgument; - deliveriesCounter: number; - }> | null; -} +// interface XPendingReply { +// pending: number; +// firstId: RedisCommandArgument | null; +// lastId: RedisCommandArgument | null; +// consumers: Array<{ +// name: RedisCommandArgument; +// deliveriesCounter: number; +// }> | null; +// } -export function transformReply(reply: XPendingRawReply): XPendingReply { - return { - pending: reply[0], - firstId: reply[1], - lastId: reply[2], - consumers: reply[3] === null ? null : reply[3].map(([name, deliveriesCounter]) => ({ - name, - deliveriesCounter: Number(deliveriesCounter) - })) - }; -} +// export function transformReply(reply: XPendingRawReply): XPendingReply { +// return { +// pending: reply[0], +// firstId: reply[1], +// lastId: reply[2], +// consumers: reply[3] === null ? null : reply[3].map(([name, deliveriesCounter]) => ({ +// name, +// deliveriesCounter: Number(deliveriesCounter) +// })) +// }; +// } diff --git a/packages/client/lib/commands/XPENDING_RANGE.spec.ts b/packages/client/lib/commands/XPENDING_RANGE.spec.ts index 0b57c704bb0..b08f71d88a2 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.spec.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.spec.ts @@ -1,53 +1,53 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XPENDING_RANGE'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XPENDING_RANGE'; -describe('XPENDING RANGE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'group', '-', '+', 1), - ['XPENDING', 'key', 'group', '-', '+', '1'] - ); - }); +// describe('XPENDING RANGE', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('key', 'group', '-', '+', 1), +// ['XPENDING', 'key', 'group', '-', '+', '1'] +// ); +// }); - it('with IDLE', () => { - assert.deepEqual( - transformArguments('key', 'group', '-', '+', 1, { - IDLE: 1, - }), - ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1'] - ); - }); +// it('with IDLE', () => { +// assert.deepEqual( +// transformArguments('key', 'group', '-', '+', 1, { +// IDLE: 1, +// }), +// ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1'] +// ); +// }); - it('with consumer', () => { - assert.deepEqual( - transformArguments('key', 'group', '-', '+', 1, { - consumer: 'consumer' - }), - ['XPENDING', 'key', 'group', '-', '+', '1', 'consumer'] - ); - }); +// it('with consumer', () => { +// assert.deepEqual( +// transformArguments('key', 'group', '-', '+', 1, { +// consumer: 'consumer' +// }), +// ['XPENDING', 'key', 'group', '-', '+', '1', 'consumer'] +// ); +// }); - it('with IDLE, consumer', () => { - assert.deepEqual( - transformArguments('key', 'group', '-', '+', 1, { - IDLE: 1, - consumer: 'consumer' - }), - ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1', 'consumer'] - ); - }); - }); +// it('with IDLE, consumer', () => { +// assert.deepEqual( +// transformArguments('key', 'group', '-', '+', 1, { +// IDLE: 1, +// consumer: 'consumer' +// }), +// ['XPENDING', 'key', 'group', 'IDLE', '1', '-', '+', '1', 'consumer'] +// ); +// }); +// }); - testUtils.testWithClient('client.xPendingRange', async client => { - await client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }); +// testUtils.testWithClient('client.xPendingRange', async client => { +// await client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }); - assert.deepEqual( - await client.xPendingRange('key', 'group', '-', '+', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.deepEqual( +// await client.xPendingRange('key', 'group', '-', '+', 1), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XPENDING_RANGE.ts b/packages/client/lib/commands/XPENDING_RANGE.ts index 87660de545d..37d95efd4f8 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.ts @@ -1,56 +1,56 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface XPendingRangeOptions { - IDLE?: number; - consumer?: RedisCommandArgument; -} - -export function transformArguments( - key: RedisCommandArgument, - group: RedisCommandArgument, - start: string, - end: string, - count: number, - options?: XPendingRangeOptions -): RedisCommandArguments { - const args = ['XPENDING', key, group]; - - if (options?.IDLE) { - args.push('IDLE', options.IDLE.toString()); - } - - args.push(start, end, count.toString()); - - if (options?.consumer) { - args.push(options.consumer); - } - - return args; -} - -type XPendingRangeRawReply = Array<[ - id: RedisCommandArgument, - consumer: RedisCommandArgument, - millisecondsSinceLastDelivery: number, - deliveriesCounter: number -]>; - -type XPendingRangeReply = Array<{ - id: RedisCommandArgument; - owner: RedisCommandArgument; - millisecondsSinceLastDelivery: number; - deliveriesCounter: number; -}>; - -export function transformReply(reply: XPendingRangeRawReply): XPendingRangeReply { - return reply.map(([id, owner, millisecondsSinceLastDelivery, deliveriesCounter]) => ({ - id, - owner, - millisecondsSinceLastDelivery, - deliveriesCounter - })); -} +// import { RedisCommandArgument, RedisCommandArguments } from '.'; + +// export const FIRST_KEY_INDEX = 1; + +// export const IS_READ_ONLY = true; + +// interface XPendingRangeOptions { +// IDLE?: number; +// consumer?: RedisCommandArgument; +// } + +// export function transformArguments( +// key: RedisCommandArgument, +// group: RedisCommandArgument, +// start: string, +// end: string, +// count: number, +// options?: XPendingRangeOptions +// ): RedisCommandArguments { +// const args = ['XPENDING', key, group]; + +// if (options?.IDLE) { +// args.push('IDLE', options.IDLE.toString()); +// } + +// args.push(start, end, count.toString()); + +// if (options?.consumer) { +// args.push(options.consumer); +// } + +// return args; +// } + +// type XPendingRangeRawReply = Array<[ +// id: RedisCommandArgument, +// consumer: RedisCommandArgument, +// millisecondsSinceLastDelivery: number, +// deliveriesCounter: number +// ]>; + +// type XPendingRangeReply = Array<{ +// id: RedisCommandArgument; +// owner: RedisCommandArgument; +// millisecondsSinceLastDelivery: number; +// deliveriesCounter: number; +// }>; + +// export function transformReply(reply: XPendingRangeRawReply): XPendingRangeReply { +// return reply.map(([id, owner, millisecondsSinceLastDelivery, deliveriesCounter]) => ({ +// id, +// owner, +// millisecondsSinceLastDelivery, +// deliveriesCounter +// })); +// } diff --git a/packages/client/lib/commands/XRANGE.spec.ts b/packages/client/lib/commands/XRANGE.spec.ts index 01c713e9595..4d937c4e32a 100644 --- a/packages/client/lib/commands/XRANGE.spec.ts +++ b/packages/client/lib/commands/XRANGE.spec.ts @@ -1,30 +1,30 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XRANGE'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XRANGE'; -describe('XRANGE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', '-', '+'), - ['XRANGE', 'key', '-', '+'] - ); - }); +// describe('XRANGE', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('key', '-', '+'), +// ['XRANGE', 'key', '-', '+'] +// ); +// }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - COUNT: 1 - }), - ['XRANGE', 'key', '-', '+', 'COUNT', '1'] - ); - }); - }); +// it('with COUNT', () => { +// assert.deepEqual( +// transformArguments('key', '-', '+', { +// COUNT: 1 +// }), +// ['XRANGE', 'key', '-', '+', 'COUNT', '1'] +// ); +// }); +// }); - testUtils.testWithClient('client.xRange', async client => { - assert.deepEqual( - await client.xRange('key', '+', '-'), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.xRange', async client => { +// assert.deepEqual( +// await client.xRange('key', '+', '-'), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XRANGE.ts b/packages/client/lib/commands/XRANGE.ts index ae56639f769..1f3cf1d856e 100644 --- a/packages/client/lib/commands/XRANGE.ts +++ b/packages/client/lib/commands/XRANGE.ts @@ -1,26 +1,26 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -interface XRangeOptions { - COUNT?: number; -} +// interface XRangeOptions { +// COUNT?: number; +// } -export function transformArguments( - key: RedisCommandArgument, - start: RedisCommandArgument, - end: RedisCommandArgument, - options?: XRangeOptions -): RedisCommandArguments { - const args = ['XRANGE', key, start, end]; +// export function transformArguments( +// key: RedisCommandArgument, +// start: RedisCommandArgument, +// end: RedisCommandArgument, +// options?: XRangeOptions +// ): RedisCommandArguments { +// const args = ['XRANGE', key, start, end]; - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } +// if (options?.COUNT) { +// args.push('COUNT', options.COUNT.toString()); +// } - return args; -} +// return args; +// } -export { transformStreamMessagesReply as transformReply } from './generic-transformers'; +// export { transformStreamMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREAD.spec.ts b/packages/client/lib/commands/XREAD.spec.ts index b607f53532e..13aa35b83fc 100644 --- a/packages/client/lib/commands/XREAD.spec.ts +++ b/packages/client/lib/commands/XREAD.spec.ts @@ -1,103 +1,103 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { FIRST_KEY_INDEX, transformArguments } from './XREAD'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { FIRST_KEY_INDEX, transformArguments } from './XREAD'; -describe('XREAD', () => { - describe('FIRST_KEY_INDEX', () => { - it('single stream', () => { - assert.equal( - FIRST_KEY_INDEX({ key: 'key', id: '' }), - 'key' - ); - }); +// describe('XREAD', () => { +// describe('FIRST_KEY_INDEX', () => { +// it('single stream', () => { +// assert.equal( +// FIRST_KEY_INDEX({ key: 'key', id: '' }), +// 'key' +// ); +// }); - it('multiple streams', () => { - assert.equal( - FIRST_KEY_INDEX([{ key: '1', id: '' }, { key: '2', id: '' }]), - '1' - ); - }); - }); +// it('multiple streams', () => { +// assert.equal( +// FIRST_KEY_INDEX([{ key: '1', id: '' }, { key: '2', id: '' }]), +// '1' +// ); +// }); +// }); - describe('transformArguments', () => { - it('single stream', () => { - assert.deepEqual( - transformArguments({ - key: 'key', - id: '0' - }), - ['XREAD', 'STREAMS', 'key', '0'] - ); - }); +// describe('transformArguments', () => { +// it('single stream', () => { +// assert.deepEqual( +// transformArguments({ +// key: 'key', +// id: '0' +// }), +// ['XREAD', 'STREAMS', 'key', '0'] +// ); +// }); - it('multiple streams', () => { - assert.deepEqual( - transformArguments([{ - key: '1', - id: '0' - }, { - key: '2', - id: '0' - }]), - ['XREAD', 'STREAMS', '1', '2', '0', '0'] - ); - }); +// it('multiple streams', () => { +// assert.deepEqual( +// transformArguments([{ +// key: '1', +// id: '0' +// }, { +// key: '2', +// id: '0' +// }]), +// ['XREAD', 'STREAMS', '1', '2', '0', '0'] +// ); +// }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments({ - key: 'key', - id: '0' - }, { - COUNT: 1 - }), - ['XREAD', 'COUNT', '1', 'STREAMS', 'key', '0'] - ); - }); +// it('with COUNT', () => { +// assert.deepEqual( +// transformArguments({ +// key: 'key', +// id: '0' +// }, { +// COUNT: 1 +// }), +// ['XREAD', 'COUNT', '1', 'STREAMS', 'key', '0'] +// ); +// }); - it('with BLOCK', () => { - assert.deepEqual( - transformArguments({ - key: 'key', - id: '0' - }, { - BLOCK: 0 - }), - ['XREAD', 'BLOCK', '0', 'STREAMS', 'key', '0'] - ); - }); +// it('with BLOCK', () => { +// assert.deepEqual( +// transformArguments({ +// key: 'key', +// id: '0' +// }, { +// BLOCK: 0 +// }), +// ['XREAD', 'BLOCK', '0', 'STREAMS', 'key', '0'] +// ); +// }); - it('with COUNT, BLOCK', () => { - assert.deepEqual( - transformArguments({ - key: 'key', - id: '0' - }, { - COUNT: 1, - BLOCK: 0 - }), - ['XREAD', 'COUNT', '1', 'BLOCK', '0', 'STREAMS', 'key', '0'] - ); - }); - }); +// it('with COUNT, BLOCK', () => { +// assert.deepEqual( +// transformArguments({ +// key: 'key', +// id: '0' +// }, { +// COUNT: 1, +// BLOCK: 0 +// }), +// ['XREAD', 'COUNT', '1', 'BLOCK', '0', 'STREAMS', 'key', '0'] +// ); +// }); +// }); - testUtils.testWithClient('client.xRead', async client => { - assert.equal( - await client.xRead({ - key: 'key', - id: '0' - }), - null - ); - }, GLOBAL.SERVERS.OPEN); +// testUtils.testWithClient('client.xRead', async client => { +// assert.equal( +// await client.xRead({ +// key: 'key', +// id: '0' +// }), +// null +// ); +// }, GLOBAL.SERVERS.OPEN); - testUtils.testWithCluster('cluster.xRead', async cluster => { - assert.equal( - await cluster.xRead({ - key: 'key', - id: '0' - }), - null - ); - }, GLOBAL.CLUSTERS.OPEN); -}); +// testUtils.testWithCluster('cluster.xRead', async cluster => { +// assert.equal( +// await cluster.xRead({ +// key: 'key', +// id: '0' +// }), +// null +// ); +// }, GLOBAL.CLUSTERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XREAD.ts b/packages/client/lib/commands/XREAD.ts index e5f85dbe7fe..f58972b1f65 100644 --- a/packages/client/lib/commands/XREAD.ts +++ b/packages/client/lib/commands/XREAD.ts @@ -1,46 +1,46 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = (streams: Array | XReadStream): RedisCommandArgument => { - return Array.isArray(streams) ? streams[0].key : streams.key; -}; +// export const FIRST_KEY_INDEX = (streams: Array | XReadStream): RedisCommandArgument => { +// return Array.isArray(streams) ? streams[0].key : streams.key; +// }; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -interface XReadStream { - key: RedisCommandArgument; - id: RedisCommandArgument; -} +// interface XReadStream { +// key: RedisCommandArgument; +// id: RedisCommandArgument; +// } -interface XReadOptions { - COUNT?: number; - BLOCK?: number; -} +// interface XReadOptions { +// COUNT?: number; +// BLOCK?: number; +// } -export function transformArguments( - streams: Array | XReadStream, - options?: XReadOptions -): RedisCommandArguments { - const args: RedisCommandArguments = ['XREAD']; +// export function transformArguments( +// streams: Array | XReadStream, +// options?: XReadOptions +// ): RedisCommandArguments { +// const args: RedisCommandArguments = ['XREAD']; - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } +// if (options?.COUNT) { +// args.push('COUNT', options.COUNT.toString()); +// } - if (typeof options?.BLOCK === 'number') { - args.push('BLOCK', options.BLOCK.toString()); - } +// if (typeof options?.BLOCK === 'number') { +// args.push('BLOCK', options.BLOCK.toString()); +// } - args.push('STREAMS'); +// args.push('STREAMS'); - const streamsArray = Array.isArray(streams) ? streams : [streams], - argsLength = args.length; - for (let i = 0; i < streamsArray.length; i++) { - const stream = streamsArray[i]; - args[argsLength + i] = stream.key; - args[argsLength + streamsArray.length + i] = stream.id; - } +// const streamsArray = Array.isArray(streams) ? streams : [streams], +// argsLength = args.length; +// for (let i = 0; i < streamsArray.length; i++) { +// const stream = streamsArray[i]; +// args[argsLength + i] = stream.key; +// args[argsLength + streamsArray.length + i] = stream.id; +// } - return args; -} +// return args; +// } -export { transformStreamsMessagesReply as transformReply } from './generic-transformers'; +// export { transformStreamsMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREADGROUP.spec.ts b/packages/client/lib/commands/XREADGROUP.spec.ts index fa196d504ad..1cccdb79e65 100644 --- a/packages/client/lib/commands/XREADGROUP.spec.ts +++ b/packages/client/lib/commands/XREADGROUP.spec.ts @@ -1,153 +1,153 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { FIRST_KEY_INDEX, transformArguments } from './XREADGROUP'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { FIRST_KEY_INDEX, transformArguments } from './XREADGROUP'; -describe('XREADGROUP', () => { - describe('FIRST_KEY_INDEX', () => { - it('single stream', () => { - assert.equal( - FIRST_KEY_INDEX('', '', { key: 'key', id: '' }), - 'key' - ); - }); +// describe('XREADGROUP', () => { +// describe('FIRST_KEY_INDEX', () => { +// it('single stream', () => { +// assert.equal( +// FIRST_KEY_INDEX('', '', { key: 'key', id: '' }), +// 'key' +// ); +// }); - it('multiple streams', () => { - assert.equal( - FIRST_KEY_INDEX('', '', [{ key: '1', id: '' }, { key: '2', id: '' }]), - '1' - ); - }); - }); +// it('multiple streams', () => { +// assert.equal( +// FIRST_KEY_INDEX('', '', [{ key: '1', id: '' }, { key: '2', id: '' }]), +// '1' +// ); +// }); +// }); - describe('transformArguments', () => { - it('single stream', () => { - assert.deepEqual( - transformArguments('group', 'consumer', { - key: 'key', - id: '0' - }), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'STREAMS', 'key', '0'] - ); - }); +// describe('transformArguments', () => { +// it('single stream', () => { +// assert.deepEqual( +// transformArguments('group', 'consumer', { +// key: 'key', +// id: '0' +// }), +// ['XREADGROUP', 'GROUP', 'group', 'consumer', 'STREAMS', 'key', '0'] +// ); +// }); - it('multiple streams', () => { - assert.deepEqual( - transformArguments('group', 'consumer', [{ - key: '1', - id: '0' - }, { - key: '2', - id: '0' - }]), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'STREAMS', '1', '2', '0', '0'] - ); - }); +// it('multiple streams', () => { +// assert.deepEqual( +// transformArguments('group', 'consumer', [{ +// key: '1', +// id: '0' +// }, { +// key: '2', +// id: '0' +// }]), +// ['XREADGROUP', 'GROUP', 'group', 'consumer', 'STREAMS', '1', '2', '0', '0'] +// ); +// }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('group', 'consumer', { - key: 'key', - id: '0' - }, { - COUNT: 1 - }), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'COUNT', '1', 'STREAMS', 'key', '0'] - ); - }); +// it('with COUNT', () => { +// assert.deepEqual( +// transformArguments('group', 'consumer', { +// key: 'key', +// id: '0' +// }, { +// COUNT: 1 +// }), +// ['XREADGROUP', 'GROUP', 'group', 'consumer', 'COUNT', '1', 'STREAMS', 'key', '0'] +// ); +// }); - it('with BLOCK', () => { - assert.deepEqual( - transformArguments('group', 'consumer', { - key: 'key', - id: '0' - }, { - BLOCK: 0 - }), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'BLOCK', '0', 'STREAMS', 'key', '0'] - ); - }); +// it('with BLOCK', () => { +// assert.deepEqual( +// transformArguments('group', 'consumer', { +// key: 'key', +// id: '0' +// }, { +// BLOCK: 0 +// }), +// ['XREADGROUP', 'GROUP', 'group', 'consumer', 'BLOCK', '0', 'STREAMS', 'key', '0'] +// ); +// }); - it('with NOACK', () => { - assert.deepEqual( - transformArguments('group', 'consumer', { - key: 'key', - id: '0' - }, { - NOACK: true - }), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'NOACK', 'STREAMS', 'key', '0'] - ); - }); +// it('with NOACK', () => { +// assert.deepEqual( +// transformArguments('group', 'consumer', { +// key: 'key', +// id: '0' +// }, { +// NOACK: true +// }), +// ['XREADGROUP', 'GROUP', 'group', 'consumer', 'NOACK', 'STREAMS', 'key', '0'] +// ); +// }); - it('with COUNT, BLOCK, NOACK', () => { - assert.deepEqual( - transformArguments('group', 'consumer', { - key: 'key', - id: '0' - }, { - COUNT: 1, - BLOCK: 0, - NOACK: true - }), - ['XREADGROUP', 'GROUP', 'group', 'consumer', 'COUNT', '1', 'BLOCK', '0', 'NOACK', 'STREAMS', 'key', '0'] - ); - }); - }); +// it('with COUNT, BLOCK, NOACK', () => { +// assert.deepEqual( +// transformArguments('group', 'consumer', { +// key: 'key', +// id: '0' +// }, { +// COUNT: 1, +// BLOCK: 0, +// NOACK: true +// }), +// ['XREADGROUP', 'GROUP', 'group', 'consumer', 'COUNT', '1', 'BLOCK', '0', 'NOACK', 'STREAMS', 'key', '0'] +// ); +// }); +// }); - describe('client.xReadGroup', () => { - testUtils.testWithClient('null', async client => { - const [, readGroupReply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); +// describe('client.xReadGroup', () => { +// testUtils.testWithClient('null', async client => { +// const [, readGroupReply] = await Promise.all([ +// client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }), +// client.xReadGroup('group', 'consumer', { +// key: 'key', +// id: '>' +// }) +// ]); - assert.equal(readGroupReply, null); - }, GLOBAL.SERVERS.OPEN); +// assert.equal(readGroupReply, null); +// }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('with a message', async client => { - const [, id, readGroupReply] = await Promise.all([ - client.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - client.xAdd('key', '*', { field: 'value' }), - client.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); +// testUtils.testWithClient('with a message', async client => { +// const [, id, readGroupReply] = await Promise.all([ +// client.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }), +// client.xAdd('key', '*', { field: 'value' }), +// client.xReadGroup('group', 'consumer', { +// key: 'key', +// id: '>' +// }) +// ]); - assert.deepEqual(readGroupReply, [{ - name: 'key', - messages: [{ - id, - message: Object.create(null, { - field: { - value: 'value', - configurable: true, - enumerable: true - } - }) - }] - }]); - }, GLOBAL.SERVERS.OPEN); - }); +// assert.deepEqual(readGroupReply, [{ +// name: 'key', +// messages: [{ +// id, +// message: Object.create(null, { +// field: { +// value: 'value', +// configurable: true, +// enumerable: true +// } +// }) +// }] +// }]); +// }, GLOBAL.SERVERS.OPEN); +// }); - testUtils.testWithCluster('cluster.xReadGroup', async cluster => { - const [, readGroupReply] = await Promise.all([ - cluster.xGroupCreate('key', 'group', '$', { - MKSTREAM: true - }), - cluster.xReadGroup('group', 'consumer', { - key: 'key', - id: '>' - }) - ]); +// testUtils.testWithCluster('cluster.xReadGroup', async cluster => { +// const [, readGroupReply] = await Promise.all([ +// cluster.xGroupCreate('key', 'group', '$', { +// MKSTREAM: true +// }), +// cluster.xReadGroup('group', 'consumer', { +// key: 'key', +// id: '>' +// }) +// ]); - assert.equal(readGroupReply, null); - }, GLOBAL.CLUSTERS.OPEN); -}); +// assert.equal(readGroupReply, null); +// }, GLOBAL.CLUSTERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XREADGROUP.ts b/packages/client/lib/commands/XREADGROUP.ts index e90e698a2ad..40064934cd6 100644 --- a/packages/client/lib/commands/XREADGROUP.ts +++ b/packages/client/lib/commands/XREADGROUP.ts @@ -1,57 +1,57 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; - -export interface XReadGroupStream { - key: RedisCommandArgument; - id: RedisCommandArgument; -} - -export interface XReadGroupOptions { - COUNT?: number; - BLOCK?: number; - NOACK?: true; -} - -export const FIRST_KEY_INDEX = ( - _group: RedisCommandArgument, - _consumer: RedisCommandArgument, - streams: Array | XReadGroupStream -): RedisCommandArgument => { - return Array.isArray(streams) ? streams[0].key : streams.key; -}; - -export const IS_READ_ONLY = true; - -export function transformArguments( - group: RedisCommandArgument, - consumer: RedisCommandArgument, - streams: Array | XReadGroupStream, - options?: XReadGroupOptions -): RedisCommandArguments { - const args = ['XREADGROUP', 'GROUP', group, consumer]; - - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } - - if (typeof options?.BLOCK === 'number') { - args.push('BLOCK', options.BLOCK.toString()); - } - - if (options?.NOACK) { - args.push('NOACK'); - } - - args.push('STREAMS'); - - const streamsArray = Array.isArray(streams) ? streams : [streams], - argsLength = args.length; - for (let i = 0; i < streamsArray.length; i++) { - const stream = streamsArray[i]; - args[argsLength + i] = stream.key; - args[argsLength + streamsArray.length + i] = stream.id; - } - - return args; -} - -export { transformStreamsMessagesReply as transformReply } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; + +// export interface XReadGroupStream { +// key: RedisCommandArgument; +// id: RedisCommandArgument; +// } + +// export interface XReadGroupOptions { +// COUNT?: number; +// BLOCK?: number; +// NOACK?: true; +// } + +// export const FIRST_KEY_INDEX = ( +// _group: RedisCommandArgument, +// _consumer: RedisCommandArgument, +// streams: Array | XReadGroupStream +// ): RedisCommandArgument => { +// return Array.isArray(streams) ? streams[0].key : streams.key; +// }; + +// export const IS_READ_ONLY = true; + +// export function transformArguments( +// group: RedisCommandArgument, +// consumer: RedisCommandArgument, +// streams: Array | XReadGroupStream, +// options?: XReadGroupOptions +// ): RedisCommandArguments { +// const args = ['XREADGROUP', 'GROUP', group, consumer]; + +// if (options?.COUNT) { +// args.push('COUNT', options.COUNT.toString()); +// } + +// if (typeof options?.BLOCK === 'number') { +// args.push('BLOCK', options.BLOCK.toString()); +// } + +// if (options?.NOACK) { +// args.push('NOACK'); +// } + +// args.push('STREAMS'); + +// const streamsArray = Array.isArray(streams) ? streams : [streams], +// argsLength = args.length; +// for (let i = 0; i < streamsArray.length; i++) { +// const stream = streamsArray[i]; +// args[argsLength + i] = stream.key; +// args[argsLength + streamsArray.length + i] = stream.id; +// } + +// return args; +// } + +// export { transformStreamsMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XREVRANGE.spec.ts b/packages/client/lib/commands/XREVRANGE.spec.ts index fd6e1a3adfe..344edd62e58 100644 --- a/packages/client/lib/commands/XREVRANGE.spec.ts +++ b/packages/client/lib/commands/XREVRANGE.spec.ts @@ -1,30 +1,30 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XREVRANGE'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XREVRANGE'; -describe('XREVRANGE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', '-', '+'), - ['XREVRANGE', 'key', '-', '+'] - ); - }); +// describe('XREVRANGE', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('key', '-', '+'), +// ['XREVRANGE', 'key', '-', '+'] +// ); +// }); - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - COUNT: 1 - }), - ['XREVRANGE', 'key', '-', '+', 'COUNT', '1'] - ); - }); - }); +// it('with COUNT', () => { +// assert.deepEqual( +// transformArguments('key', '-', '+', { +// COUNT: 1 +// }), +// ['XREVRANGE', 'key', '-', '+', 'COUNT', '1'] +// ); +// }); +// }); - testUtils.testWithClient('client.xRevRange', async client => { - assert.deepEqual( - await client.xRevRange('key', '+', '-'), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.xRevRange', async client => { +// assert.deepEqual( +// await client.xRevRange('key', '+', '-'), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XREVRANGE.ts b/packages/client/lib/commands/XREVRANGE.ts index 96bbeba83ce..de095dc0297 100644 --- a/packages/client/lib/commands/XREVRANGE.ts +++ b/packages/client/lib/commands/XREVRANGE.ts @@ -1,26 +1,26 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -interface XRangeRevOptions { - COUNT?: number; -} +// interface XRangeRevOptions { +// COUNT?: number; +// } -export function transformArguments( - key: RedisCommandArgument, - start: RedisCommandArgument, - end: RedisCommandArgument, - options?: XRangeRevOptions -): RedisCommandArguments { - const args = ['XREVRANGE', key, start, end]; +// export function transformArguments( +// key: RedisCommandArgument, +// start: RedisCommandArgument, +// end: RedisCommandArgument, +// options?: XRangeRevOptions +// ): RedisCommandArguments { +// const args = ['XREVRANGE', key, start, end]; - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } +// if (options?.COUNT) { +// args.push('COUNT', options.COUNT.toString()); +// } - return args; -} +// return args; +// } -export { transformStreamMessagesReply as transformReply } from './generic-transformers'; +// export { transformStreamMessagesReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/XSETID.ts b/packages/client/lib/commands/XSETID.ts index 76acc7ebab4..df7cc10f906 100644 --- a/packages/client/lib/commands/XSETID.ts +++ b/packages/client/lib/commands/XSETID.ts @@ -1,28 +1,28 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -interface XSetIdOptions { - ENTRIESADDED?: number; - MAXDELETEDID?: RedisCommandArgument; -} +// interface XSetIdOptions { +// ENTRIESADDED?: number; +// MAXDELETEDID?: RedisCommandArgument; +// } -export function transformArguments( - key: RedisCommandArgument, - lastId: RedisCommandArgument, - options?: XSetIdOptions -): RedisCommandArguments { - const args = ['XSETID', key, lastId]; +// export function transformArguments( +// key: RedisCommandArgument, +// lastId: RedisCommandArgument, +// options?: XSetIdOptions +// ): RedisCommandArguments { +// const args = ['XSETID', key, lastId]; - if (options?.ENTRIESADDED) { - args.push('ENTRIESADDED', options.ENTRIESADDED.toString()); - } +// if (options?.ENTRIESADDED) { +// args.push('ENTRIESADDED', options.ENTRIESADDED.toString()); +// } - if (options?.MAXDELETEDID) { - args.push('MAXDELETEDID', options.MAXDELETEDID); - } +// if (options?.MAXDELETEDID) { +// args.push('MAXDELETEDID', options.MAXDELETEDID); +// } - return args; -} +// return args; +// } -export declare function transformReply(): 'OK'; +// export declare function transformReply(): 'OK'; diff --git a/packages/client/lib/commands/XTRIM.spec.ts b/packages/client/lib/commands/XTRIM.spec.ts index a8f8078eb28..bcbb1c928c5 100644 --- a/packages/client/lib/commands/XTRIM.spec.ts +++ b/packages/client/lib/commands/XTRIM.spec.ts @@ -1,49 +1,49 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './XTRIM'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './XTRIM'; -describe('XTRIM', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'MAXLEN', 1), - ['XTRIM', 'key', 'MAXLEN', '1'] - ); - }); +// describe('XTRIM', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('key', 'MAXLEN', 1), +// ['XTRIM', 'key', 'MAXLEN', '1'] +// ); +// }); - it('with strategyModifier', () => { - assert.deepEqual( - transformArguments('key', 'MAXLEN', 1, { - strategyModifier: '=' - }), - ['XTRIM', 'key', 'MAXLEN', '=', '1'] - ); - }); +// it('with strategyModifier', () => { +// assert.deepEqual( +// transformArguments('key', 'MAXLEN', 1, { +// strategyModifier: '=' +// }), +// ['XTRIM', 'key', 'MAXLEN', '=', '1'] +// ); +// }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('key', 'MAXLEN', 1, { - LIMIT: 1 - }), - ['XTRIM', 'key', 'MAXLEN', '1', 'LIMIT', '1'] - ); - }); +// it('with LIMIT', () => { +// assert.deepEqual( +// transformArguments('key', 'MAXLEN', 1, { +// LIMIT: 1 +// }), +// ['XTRIM', 'key', 'MAXLEN', '1', 'LIMIT', '1'] +// ); +// }); - it('with strategyModifier, LIMIT', () => { - assert.deepEqual( - transformArguments('key', 'MAXLEN', 1, { - strategyModifier: '=', - LIMIT: 1 - }), - ['XTRIM', 'key', 'MAXLEN', '=', '1', 'LIMIT', '1'] - ); - }); - }); +// it('with strategyModifier, LIMIT', () => { +// assert.deepEqual( +// transformArguments('key', 'MAXLEN', 1, { +// strategyModifier: '=', +// LIMIT: 1 +// }), +// ['XTRIM', 'key', 'MAXLEN', '=', '1', 'LIMIT', '1'] +// ); +// }); +// }); - testUtils.testWithClient('client.xTrim', async client => { - assert.equal( - await client.xTrim('key', 'MAXLEN', 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.xTrim', async client => { +// assert.equal( +// await client.xTrim('key', 'MAXLEN', 1), +// 0 +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/XTRIM.ts b/packages/client/lib/commands/XTRIM.ts index 15b934c56ef..780aeb30d7b 100644 --- a/packages/client/lib/commands/XTRIM.ts +++ b/packages/client/lib/commands/XTRIM.ts @@ -1,31 +1,31 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -interface XTrimOptions { - strategyModifier?: '=' | '~'; - LIMIT?: number; -} +// interface XTrimOptions { +// strategyModifier?: '=' | '~'; +// LIMIT?: number; +// } -export function transformArguments( - key: RedisCommandArgument, - strategy: 'MAXLEN' | 'MINID', - threshold: number, - options?: XTrimOptions -): RedisCommandArguments { - const args = ['XTRIM', key, strategy]; +// export function transformArguments( +// key: RedisCommandArgument, +// strategy: 'MAXLEN' | 'MINID', +// threshold: number, +// options?: XTrimOptions +// ): RedisCommandArguments { +// const args = ['XTRIM', key, strategy]; - if (options?.strategyModifier) { - args.push(options.strategyModifier); - } +// if (options?.strategyModifier) { +// args.push(options.strategyModifier); +// } - args.push(threshold.toString()); +// args.push(threshold.toString()); - if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.toString()); - } +// if (options?.LIMIT) { +// args.push('LIMIT', options.LIMIT.toString()); +// } - return args; -} +// return args; +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ZADD.spec.ts b/packages/client/lib/commands/ZADD.spec.ts index 4f497bdca90..722856caa81 100644 --- a/packages/client/lib/commands/ZADD.spec.ts +++ b/packages/client/lib/commands/ZADD.spec.ts @@ -1,127 +1,158 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZADD'; +import ZADD from './ZADD'; describe('ZADD', () => { - describe('transformArguments', () => { - it('single member', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }), - ['ZADD', 'key', '1', '1'] - ); - }); + describe('transformArguments', () => { + it('single member', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }), + ['ZADD', 'key', '1', '1'] + ); + }); + + it('multiple members', () => { + assert.deepEqual( + ZADD.transformArguments('key', [{ + value: '1', + score: 1 + }, { + value: '2', + score: 2 + }]), + ['ZADD', 'key', '1', '1', '2', '2'] + ); + }); - it('multiple members', () => { - assert.deepEqual( - transformArguments('key', [{ - value: '1', - score: 1 - }, { - value: '2', - score: 2 - }]), - ['ZADD', 'key', '1', '1', '2', '2'] - ); - }); + describe('with condition', () => { + it('condition property', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + condition: 'NX' + }), + ['ZADD', 'key', 'NX', '1', '1'] + ); + }); - it('with NX', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - NX: true - }), - ['ZADD', 'key', 'NX', '1', '1'] - ); - }); + it('with NX (backwards compatibility)', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + NX: true + }), + ['ZADD', 'key', 'NX', '1', '1'] + ); + }); + + it('with XX (backwards compatibility)', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + XX: true + }), + ['ZADD', 'key', 'XX', '1', '1'] + ); + }); + }); - it('with XX', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - XX: true - }), - ['ZADD', 'key', 'XX', '1', '1'] - ); - }); + describe('with comparison', () => { + it('with LT', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + comparison: 'LT' + }), + ['ZADD', 'key', 'LT', '1', '1'] + ); + }); - it('with GT', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - GT: true - }), - ['ZADD', 'key', 'GT', '1', '1'] - ); - }); + it('with LT (backwards compatibility)', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + LT: true + }), + ['ZADD', 'key', 'LT', '1', '1'] + ); + }); - it('with LT', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - LT: true - }), - ['ZADD', 'key', 'LT', '1', '1'] - ); - }); + it('with GT (backwards compatibility)', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + GT: true + }), + ['ZADD', 'key', 'GT', '1', '1'] + ); + }); + }); - it('with CH', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - CH: true - }), - ['ZADD', 'key', 'CH', '1', '1'] - ); - }); + it('with CH', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + CH: true + }), + ['ZADD', 'key', 'CH', '1', '1'] + ); + }); - it('with INCR', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - INCR: true - }), - ['ZADD', 'key', 'INCR', '1', '1'] - ); - }); + it('with INCR', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + INCR: true + }), + ['ZADD', 'key', 'INCR', '1', '1'] + ); + }); - it('with XX, GT, CH, INCR', () => { - assert.deepEqual( - transformArguments('key', { - value: '1', - score: 1 - }, { - XX: true, - GT: true, - CH: true, - INCR: true - }), - ['ZADD', 'key', 'XX', 'GT', 'CH', 'INCR', '1', '1'] - ); - }); + it('with condition, comparison, CH, INCR', () => { + assert.deepEqual( + ZADD.transformArguments('key', { + value: '1', + score: 1 + }, { + condition: 'XX', + comparison: 'LT', + CH: true, + INCR: true + }), + ['ZADD', 'key', 'XX', 'LT', 'CH', 'INCR', '1', '1'] + ); }); + }); - testUtils.testWithClient('client.zAdd', async client => { - assert.equal( - await client.zAdd('key', { - value: '1', - score: 1 - }), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zAdd', async client => { + assert.equal( + await client.zAdd('key', { + value: '1', + score: 1 + }), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZADD.ts b/packages/client/lib/commands/ZADD.ts index 9ac67d59cce..49a839fb6f3 100644 --- a/packages/client/lib/commands/ZADD.ts +++ b/packages/client/lib/commands/ZADD.ts @@ -1,71 +1,81 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformNumberInfinityArgument, ZMember } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -interface NX { - NX?: true; +import { RedisArgument, NumberReply, DoubleReply, Command } from '../RESP/types'; +import { ZMember, transformDoubleArgument, transformDoubleReply } from './generic-transformers'; + +export interface ZAddOptions { + condition?: 'NX' | 'XX'; + /** + * @deprecated Use `{ condition: 'NX' }` instead. + */ + NX?: boolean; + /** + * @deprecated Use `{ condition: 'XX' }` instead. + */ + XX?: boolean; + comparison?: 'LT' | 'GT'; + /** + * @deprecated Use `{ comparison: 'LT' }` instead. + */ + LT?: boolean; + /** + * @deprecated Use `{ comparison: 'GT' }` instead. + */ + GT?: boolean; + CH?: boolean; + INCR?: boolean; } -interface XX { - XX?: true; -} - -interface LT { - LT?: true; -} - -interface GT { - GT?: true; -} - -interface CH { - CH?: true; -} - -interface INCR { - INCR?: true; -} - -type ZAddOptions = (NX | (XX & LT & GT)) & CH & INCR; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, members: ZMember | Array, options?: ZAddOptions -): RedisCommandArguments { + ) { const args = ['ZADD', key]; - if ((options)?.NX) { - args.push('NX'); - } else { - if ((options)?.XX) { - args.push('XX'); - } - - if ((options)?.GT) { - args.push('GT'); - } else if ((options)?.LT) { - args.push('LT'); - } + if (options?.condition) { + args.push(options.condition); + } else if (options?.NX) { + args.push('NX'); + } else if (options?.XX) { + args.push('XX'); + } + + if (options?.comparison) { + args.push(options.comparison); + } else if (options?.LT) { + args.push('LT'); + } else if (options?.GT) { + args.push('GT'); } - if ((options)?.CH) { - args.push('CH'); + if (options?.CH) { + args.push('CH'); } - if ((options)?.INCR) { - args.push('INCR'); + if (options?.INCR) { + args.push('INCR'); } - for (const { score, value } of (Array.isArray(members) ? members : [members])) { - args.push( - transformNumberInfinityArgument(score), - value - ); + if (Array.isArray(members)) { + for (const member of members) { + pushMember(args, member); + } + } else { + pushMember(args, members); } return args; + }, + transformReply: { + 2: transformDoubleReply, + 3: undefined as unknown as () => NumberReply | DoubleReply + } +} as const satisfies Command; + +function pushMember(args: Array, member: ZMember) { + args.push( + transformDoubleArgument(member.score), + member.value + ); } - -export { transformNumberInfinityReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZCARD.spec.ts b/packages/client/lib/commands/ZCARD.spec.ts index 2e90da772b6..f0c566832df 100644 --- a/packages/client/lib/commands/ZCARD.spec.ts +++ b/packages/client/lib/commands/ZCARD.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZCARD'; +import ZCARD from './ZCARD'; describe('ZCARD', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['ZCARD', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZCARD.transformArguments('key'), + ['ZCARD', 'key'] + ); + }); - testUtils.testWithClient('client.zCard', async client => { - assert.equal( - await client.zCard('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zCard', async client => { + assert.equal( + await client.zCard('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZCARD.ts b/packages/client/lib/commands/ZCARD.ts index f208c181369..c11cb69db3c 100644 --- a/packages/client/lib/commands/ZCARD.ts +++ b/packages/client/lib/commands/ZCARD.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['ZCARD', key]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZCOUNT.spec.ts b/packages/client/lib/commands/ZCOUNT.spec.ts index e185ed3cd45..dd2a7124d1f 100644 --- a/packages/client/lib/commands/ZCOUNT.spec.ts +++ b/packages/client/lib/commands/ZCOUNT.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZCOUNT'; +import ZCOUNT from './ZCOUNT'; describe('ZCOUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 1), - ['ZCOUNT', 'key', '0', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZCOUNT.transformArguments('key', 0, 1), + ['ZCOUNT', 'key', '0', '1'] + ); + }); - testUtils.testWithClient('client.zCount', async client => { - assert.equal( - await client.zCount('key', 0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zCount', async client => { + assert.equal( + await client.zCount('key', 0, 1), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.SERVERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZCOUNT.ts b/packages/client/lib/commands/ZCOUNT.ts index f9700cc9099..187a316b15a 100644 --- a/packages/client/lib/commands/ZCOUNT.ts +++ b/packages/client/lib/commands/ZCOUNT.ts @@ -1,21 +1,20 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { transformStringDoubleArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument | number, - max: RedisCommandArgument | number -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + min: number | RedisArgument, + max: number | RedisArgument + ) { return [ - 'ZCOUNT', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) + 'ZCOUNT', + key, + transformStringDoubleArgument(min), + transformStringDoubleArgument(max) ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZDIFF.spec.ts b/packages/client/lib/commands/ZDIFF.spec.ts index 8bb1a101f53..6d4ed91df31 100644 --- a/packages/client/lib/commands/ZDIFF.spec.ts +++ b/packages/client/lib/commands/ZDIFF.spec.ts @@ -1,30 +1,33 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZDIFF'; +import ZDIFF from './ZDIFF'; describe('ZDIFF', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['ZDIFF', '1', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ZDIFF.transformArguments('key'), + ['ZDIFF', '1', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZDIFF', '2', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + ZDIFF.transformArguments(['1', '2']), + ['ZDIFF', '2', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.zDiff', async client => { - assert.deepEqual( - await client.zDiff('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zDiff', async client => { + assert.deepEqual( + await client.zDiff('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZDIFF.ts b/packages/client/lib/commands/ZDIFF.ts index f3818a139f1..f16c8717cdb 100644 --- a/packages/client/lib/commands/ZDIFF.ts +++ b/packages/client/lib/commands/ZDIFF.ts @@ -1,14 +1,11 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; - -export function transformArguments( - keys: Array | RedisCommandArgument -): RedisCommandArguments { - return pushVerdictArgument(['ZDIFF'], keys); -} - -export declare function transformReply(): Array; +import { ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(keys: RedisVariadicArgument) { + return pushVariadicArgument(['ZDIFF'], keys); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZDIFFSTORE.spec.ts b/packages/client/lib/commands/ZDIFFSTORE.spec.ts index c63902b2666..4901c5b1e14 100644 --- a/packages/client/lib/commands/ZDIFFSTORE.spec.ts +++ b/packages/client/lib/commands/ZDIFFSTORE.spec.ts @@ -1,30 +1,33 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZDIFFSTORE'; +import ZDIFFSTORE from './ZDIFFSTORE'; describe('ZDIFFSTORE', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['ZDIFFSTORE', 'destination', '1', 'key'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ZDIFFSTORE.transformArguments('destination', 'key'), + ['ZDIFFSTORE', 'destination', '1', 'key'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['ZDIFFSTORE', 'destination', '2', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + ZDIFFSTORE.transformArguments('destination', ['1', '2']), + ['ZDIFFSTORE', 'destination', '2', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.zDiffStore', async client => { - assert.equal( - await client.zDiffStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zDiffStore', async client => { + assert.equal( + await client.zDiffStore('{tag}destination', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZDIFFSTORE.ts b/packages/client/lib/commands/ZDIFFSTORE.ts index 3b9af9511c5..e4614a1cb14 100644 --- a/packages/client/lib/commands/ZDIFFSTORE.ts +++ b/packages/client/lib/commands/ZDIFFSTORE.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - destination: RedisCommandArgument, - keys: Array | RedisCommandArgument -): RedisCommandArguments { - return pushVerdictArgument(['ZDIFFSTORE', destination], keys); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + destination: RedisArgument, + inputKeys: RedisVariadicArgument + ) { + return pushVariadicArgument(['ZDIFFSTORE', destination], inputKeys); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts index 3b9cb725aaa..0082be3cf86 100644 --- a/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.spec.ts @@ -1,30 +1,33 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZDIFF_WITHSCORES'; +import ZDIFF_WITHSCORES from './ZDIFF_WITHSCORES'; describe('ZDIFF WITHSCORES', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key'), - ['ZDIFF', '1', 'key', 'WITHSCORES'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ZDIFF_WITHSCORES.transformArguments('key'), + ['ZDIFF', '1', 'key', 'WITHSCORES'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZDIFF', '2', '1', '2', 'WITHSCORES'] - ); - }); + it('array', () => { + assert.deepEqual( + ZDIFF_WITHSCORES.transformArguments(['1', '2']), + ['ZDIFF', '2', '1', '2', 'WITHSCORES'] + ); }); + }); - testUtils.testWithClient('client.zDiffWithScores', async client => { - assert.deepEqual( - await client.zDiffWithScores('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zDiffWithScores', async client => { + assert.deepEqual( + await client.zDiffWithScores('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts index 9caa13c9f8b..0fa1a6d271a 100644 --- a/packages/client/lib/commands/ZDIFF_WITHSCORES.ts +++ b/packages/client/lib/commands/ZDIFF_WITHSCORES.ts @@ -1,13 +1,19 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZDiffArguments } from './ZDIFF'; +import { ArrayReply, BlobStringReply, Command, NumberReply } from '../RESP/types'; +import ZDIFF from './ZDIFF'; +import { transformSortedSetWithScoresReply } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZDIFF'; - -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZDiffArguments(...args), - 'WITHSCORES' - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments(keys: Parameters[0]) { + const args = ZDIFF.transformArguments(keys); + args.push('WITHSCORES'); + return args; + }, + transformReply: { + 2: transformSortedSetWithScoresReply, + 3: (reply: ArrayReply<[BlobStringReply, NumberReply]>) => { + return reply.map(([value, score]) => ({ value, score })); + } + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINCRBY.spec.ts b/packages/client/lib/commands/ZINCRBY.spec.ts index bf2a34b0965..531f39cc3f8 100644 --- a/packages/client/lib/commands/ZINCRBY.spec.ts +++ b/packages/client/lib/commands/ZINCRBY.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZINCRBY'; +import ZINCRBY from './ZINCRBY'; describe('ZINCRBY', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1, 'member'), - ['ZINCRBY', 'key', '1', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZINCRBY.transformArguments('key', 1, 'member'), + ['ZINCRBY', 'key', '1', 'member'] + ); + }); - testUtils.testWithClient('client.zIncrBy', async client => { - assert.equal( - await client.zIncrBy('destination', 1, 'member'), - 1 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zIncrBy', async client => { + assert.equal( + await client.zIncrBy('destination', 1, 'member'), + 1 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZINCRBY.ts b/packages/client/lib/commands/ZINCRBY.ts index 68d89351391..c20da15161d 100644 --- a/packages/client/lib/commands/ZINCRBY.ts +++ b/packages/client/lib/commands/ZINCRBY.ts @@ -1,19 +1,22 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformNumberInfinityArgument } from './generic-transformers'; +import { RedisArgument, DoubleReply, Command } from '../RESP/types'; +import { transformDoubleArgument, transformDoubleReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + key: RedisArgument, increment: number, - member: RedisCommandArgument -): RedisCommandArguments { + member: RedisArgument + ) { return [ - 'ZINCRBY', - key, - transformNumberInfinityArgument(increment), - member + 'ZINCRBY', + key, + transformDoubleArgument(increment), + member ]; -} - -export { transformNumberInfinityReply as transformReply } from './generic-transformers'; + }, + transformReply: { + 2: transformDoubleReply, + 3: undefined as unknown as () => DoubleReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTER.spec.ts b/packages/client/lib/commands/ZINTER.spec.ts index 4d2d86c8869..d90a0074f25 100644 --- a/packages/client/lib/commands/ZINTER.spec.ts +++ b/packages/client/lib/commands/ZINTER.spec.ts @@ -1,58 +1,65 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZINTER'; +import ZINTER from './ZINTER'; describe('ZINTER', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('key'), - ['ZINTER', '1', 'key'] - ); - }); + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + ZINTER.transformArguments('key'), + ['ZINTER', '1', 'key'] + ); + }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZINTER', '2', '1', '2'] - ); - }); + it('keys (Array)', () => { + assert.deepEqual( + ZINTER.transformArguments(['1', '2']), + ['ZINTER', '2', '1', '2'] + ); + }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1] - }), - ['ZINTER', '1', 'key', 'WEIGHTS', '1'] - ); - }); + it('key & weight', () => { + assert.deepEqual( + ZINTER.transformArguments({ + key: 'key', + weight: 1 + }), + ['ZINTER', '1', 'key', 'WEIGHTS', '1'] + ); + }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - AGGREGATE: 'SUM' - }), - ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM'] - ); - }); + it('keys & weights', () => { + assert.deepEqual( + ZINTER.transformArguments([{ + key: 'a', + weight: 1 + }, { + key: 'b', + weight: 2 + }]), + ['ZINTER', '2', 'a', 'b', 'WEIGHTS', '1', '2'] + ); + }); - it('with WEIGHTS, AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1], - AGGREGATE: 'SUM' - }), - ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM'] - ); - }); + it('with AGGREGATE', () => { + assert.deepEqual( + ZINTER.transformArguments('key', { + AGGREGATE: 'SUM' + }), + ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM'] + ); }); + }); - testUtils.testWithClient('client.zInter', async client => { - assert.deepEqual( - await client.zInter('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zInter', async client => { + assert.deepEqual( + await client.zInter('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZINTER.ts b/packages/client/lib/commands/ZINTER.ts index 88d7f801882..a4c0be6debe 100644 --- a/packages/client/lib/commands/ZINTER.ts +++ b/packages/client/lib/commands/ZINTER.ts @@ -1,33 +1,75 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { transformDoubleArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +export type ZInterKeyAndWeight = { + key: RedisArgument; + weight: number; +}; -export const IS_READ_ONLY = true; +export type ZInterKeys = T | [T, ...Array]; -interface ZInterOptions { - WEIGHTS?: Array; - AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; +export interface ZInterOptions { + AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; } -export function transformArguments( - keys: Array | RedisCommandArgument, - options?: ZInterOptions -): RedisCommandArguments { - const args = pushVerdictArgument(['ZINTER'], keys); - - if (options?.WEIGHTS) { - args.push( - 'WEIGHTS', - ...options.WEIGHTS.map(weight => weight.toString()) - ); +export function pushZInterArguments( + args: Array, + keys: ZInterKeys | ZInterKeys, + options?: ZInterOptions +) { + if (Array.isArray(keys)) { + args.push(keys.length.toString()); + + if (keys.length) { + if (isPlainKeys(keys)) { + args = args.concat(keys); + } else { + const start = args.length; + args[start + keys.length] = 'WEIGHTS'; + for (let i = 0; i < keys.length; i++) { + const index = start + i; + args[index] = keys[i].key; + args[index + 1 + keys.length] = transformDoubleArgument(keys[i].weight); + } + } } + } else { + args.push('1'); - if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); + if (isPlainKey(keys)) { + args.push(keys); + } else { + args.push( + keys.key, + 'WEIGHTS', + transformDoubleArgument(keys.weight) + ); } + } + + if (options?.AGGREGATE) { + args.push('AGGREGATE', options.AGGREGATE); + } + + return args; +} + +function isPlainKey(key: RedisArgument | ZInterKeyAndWeight): key is RedisArgument { + return typeof key === 'string' || Buffer.isBuffer(key); +} - return args; +function isPlainKeys(keys: Array | Array): keys is Array { + return isPlainKey(keys[0]); } -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + keys: ZInterKeys | ZInterKeys, + options?: ZInterOptions + ) { + return pushZInterArguments(['ZINTER'], keys, options); + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTERCARD.spec.ts b/packages/client/lib/commands/ZINTERCARD.spec.ts index 492c1a90433..10b88cbed76 100644 --- a/packages/client/lib/commands/ZINTERCARD.spec.ts +++ b/packages/client/lib/commands/ZINTERCARD.spec.ts @@ -1,30 +1,33 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZINTERCARD'; +import ZINTERCARD from './ZINTERCARD'; describe('ZINTERCARD', () => { - testUtils.isVersionGreaterThanHook([7]); + testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZINTERCARD', '2', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ZINTERCARD.transformArguments(['1', '2']), + ['ZINTERCARD', '2', '1', '2'] + ); + }); - it('with limit', () => { - assert.deepEqual( - transformArguments(['1', '2'], 1), - ['ZINTERCARD', '2', '1', '2', 'LIMIT', '1'] - ); - }); + it('with limit', () => { + assert.deepEqual( + ZINTERCARD.transformArguments(['1', '2'], 1), + ['ZINTERCARD', '2', '1', '2', 'LIMIT', '1'] + ); }); + }); - testUtils.testWithClient('client.zInterCard', async client => { - assert.deepEqual( - await client.zInterCard('key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zInterCard', async client => { + assert.deepEqual( + await client.zInterCard('key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZINTERCARD.ts b/packages/client/lib/commands/ZINTERCARD.ts index ff45ab2aa01..9953d88eecc 100644 --- a/packages/client/lib/commands/ZINTERCARD.ts +++ b/packages/client/lib/commands/ZINTERCARD.ts @@ -1,21 +1,27 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +import { NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; - -export const IS_READ_ONLY = true; +export interface ZInterCardOptions { + LIMIT?: number; +} -export function transformArguments( - keys: Array | RedisCommandArgument, - limit?: number -): RedisCommandArguments { - const args = pushVerdictArgument(['ZINTERCARD'], keys); +export default { + FIRST_KEY_INDEX: 2, + IS_READ_ONLY: true, + transformArguments( + keys: RedisVariadicArgument, + options?: ZInterCardOptions['LIMIT'] | ZInterCardOptions + ) { + const args = pushVariadicArgument(['ZINTERCARD'], keys); - if (limit) { - args.push('LIMIT', limit.toString()); + // backwards compatibility + if (typeof options === 'number') { + args.push('LIMIT', options.toString()); + } else if (options?.LIMIT) { + args.push('LIMIT', options.LIMIT.toString()); } return args; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTERSTORE.spec.ts b/packages/client/lib/commands/ZINTERSTORE.spec.ts index 224961f0786..6c7a534e3db 100644 --- a/packages/client/lib/commands/ZINTERSTORE.spec.ts +++ b/packages/client/lib/commands/ZINTERSTORE.spec.ts @@ -1,56 +1,63 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZINTERSTORE'; +import ZINTERSTORE from './ZINTERSTORE'; describe('ZINTERSTORE', () => { - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['ZINTERSTORE', 'destination', '1', 'key'] - ); - }); + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + ZINTERSTORE.transformArguments('destination', 'key'), + ['ZINTERSTORE', 'destination', '1', 'key'] + ); + }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['ZINTERSTORE', 'destination', '2', '1', '2'] - ); - }); + it('keys (Array)', () => { + assert.deepEqual( + ZINTERSTORE.transformArguments('destination', ['1', '2']), + ['ZINTERSTORE', 'destination', '2', '1', '2'] + ); + }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - WEIGHTS: [1] - }), - ['ZINTERSTORE', 'destination', '1', 'key', 'WEIGHTS', '1'] - ); - }); + it('key & weight', () => { + assert.deepEqual( + ZINTERSTORE.transformArguments('destination', { + key: 'key', + weight: 1 + }), + ['ZINTERSTORE', 'destination', '1', 'key', 'WEIGHTS', '1'] + ); + }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - AGGREGATE: 'SUM' - }), - ['ZINTERSTORE', 'destination', '1', 'key', 'AGGREGATE', 'SUM'] - ); - }); + it('keys & weights', () => { + assert.deepEqual( + ZINTERSTORE.transformArguments('destination', [{ + key: 'a', + weight: 1 + }, { + key: 'b', + weight: 2 + }]), + ['ZINTERSTORE', 'destination', '2', 'a', 'b', 'WEIGHTS', '1', '2'] + ); + }); - it('with WEIGHTS, AGGREGATE', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - WEIGHTS: [1], - AGGREGATE: 'SUM' - }), - ['ZINTERSTORE', 'destination', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM'] - ); - }); + it('with AGGREGATE', () => { + assert.deepEqual( + ZINTERSTORE.transformArguments('destination', 'key', { + AGGREGATE: 'SUM' + }), + ['ZINTERSTORE', 'destination', '1', 'key', 'AGGREGATE', 'SUM'] + ); }); + }); - testUtils.testWithClient('client.zInterStore', async client => { - assert.equal( - await client.zInterStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zInterStore', async client => { + assert.equal( + await client.zInterStore('{tag}destination', '{tag}key'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZINTERSTORE.ts b/packages/client/lib/commands/ZINTERSTORE.ts index 540f10ae2d8..9188b0d90f1 100644 --- a/packages/client/lib/commands/ZINTERSTORE.ts +++ b/packages/client/lib/commands/ZINTERSTORE.ts @@ -1,32 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -interface ZInterStoreOptions { - WEIGHTS?: Array; - AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; -} - -export function transformArguments( - destination: RedisCommandArgument, - keys: Array | RedisCommandArgument, - options?: ZInterStoreOptions -): RedisCommandArguments { - const args = pushVerdictArgument(['ZINTERSTORE', destination], keys); - - if (options?.WEIGHTS) { - args.push( - 'WEIGHTS', - ...options.WEIGHTS.map(weight => weight.toString()) - ); - } - - if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); - } - - return args; -} - -export declare function transformReply(): number; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { pushZInterArguments, ZInterKeyAndWeight, ZInterKeys, ZInterOptions } from './ZINTER'; + +export default { + FIRST_KEY_INDEX: 1, + transformArguments( + destination: RedisArgument, + keys: ZInterKeys | ZInterKeys, + options?: ZInterOptions + ) { + return pushZInterArguments(['ZINTERSTORE', destination], keys, options); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts index 0eaeb26a244..e2689733b13 100644 --- a/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts @@ -1,58 +1,65 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZINTER_WITHSCORES'; +import ZINTER_WITHSCORES from './ZINTER_WITHSCORES'; describe('ZINTER WITHSCORES', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('key'), - ['ZINTER', '1', 'key', 'WITHSCORES'] - ); - }); + describe('transformArguments', () => { + it('key (string)', () => { + assert.deepEqual( + ZINTER_WITHSCORES.transformArguments('key'), + ['ZINTER', '1', 'key', 'WITHSCORES'] + ); + }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZINTER', '2', '1', '2', 'WITHSCORES'] - ); - }); + it('keys (Array)', () => { + assert.deepEqual( + ZINTER_WITHSCORES.transformArguments(['1', '2']), + ['ZINTER', '2', '1', '2', 'WITHSCORES'] + ); + }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1] - }), - ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES'] - ); - }); + it('key & weight', () => { + assert.deepEqual( + ZINTER_WITHSCORES.transformArguments({ + key: 'key', + weight: 1 + }), + ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES'] + ); + }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - AGGREGATE: 'SUM' - }), - ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] - ); - }); + it('keys & weights', () => { + assert.deepEqual( + ZINTER_WITHSCORES.transformArguments([{ + key: 'a', + weight: 1 + }, { + key: 'b', + weight: 2 + }]), + ['ZINTER', '2', 'a', 'b', 'WEIGHTS', '1', '2', 'WITHSCORES'] + ); + }); - it('with WEIGHTS, AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1], - AGGREGATE: 'SUM' - }), - ['ZINTER', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM', 'WITHSCORES'] - ); - }); + it('with AGGREGATE', () => { + assert.deepEqual( + ZINTER_WITHSCORES.transformArguments('key', { + AGGREGATE: 'SUM' + }), + ['ZINTER', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] + ); }); + }); - testUtils.testWithClient('client.zInterWithScores', async client => { - assert.deepEqual( - await client.zInterWithScores('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zInterWithScores', async client => { + assert.deepEqual( + await client.zInterWithScores('key'), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZINTER_WITHSCORES.ts b/packages/client/lib/commands/ZINTER_WITHSCORES.ts index c9416e9222a..07616251452 100644 --- a/packages/client/lib/commands/ZINTER_WITHSCORES.ts +++ b/packages/client/lib/commands/ZINTER_WITHSCORES.ts @@ -1,13 +1,38 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZInterArguments } from './ZINTER'; +// import { RedisCommandArguments } from '.'; +// import { transformArguments as transformZInterArguments } from './ZINTER'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER'; -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZInterArguments(...args), - 'WITHSCORES' - ]; -} +// export function transformArguments(...args: Parameters): RedisCommandArguments { +// return [ +// ...transformZInterArguments(...args), +// 'WITHSCORES' +// ]; +// } -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; + + +// // transformSortedSetWithScoresReply + +import { ArrayReply, BlobStringReply, Command, DoubleReply } from '../RESP/types'; +import ZINTER from './ZINTER'; +import { transformSortedSetWithScoresReply } from './generic-transformers'; + +export default { + FIRST_KEY_INDEX: ZINTER.FIRST_KEY_INDEX, + IS_READ_ONLY: ZINTER.IS_READ_ONLY, + transformArguments(...args: Parameters) { + const redisArgs = ZINTER.transformArguments(...args); + redisArgs.push('WITHSCORES'); + return redisArgs; + }, + transformReply: { + 2: transformSortedSetWithScoresReply, + 3: (reply: ArrayReply<[BlobStringReply, DoubleReply]>) => { + return reply.map(([member, score]) => ({ + member, + score + })); + } + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZLEXCOUNT.spec.ts b/packages/client/lib/commands/ZLEXCOUNT.spec.ts index 85809f1a9a9..b787f70cf86 100644 --- a/packages/client/lib/commands/ZLEXCOUNT.spec.ts +++ b/packages/client/lib/commands/ZLEXCOUNT.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZLEXCOUNT'; +import ZLEXCOUNT from './ZLEXCOUNT'; describe('ZLEXCOUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '[a', '[b'), - ['ZLEXCOUNT', 'key', '[a', '[b'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZLEXCOUNT.transformArguments('key', '[a', '[b'), + ['ZLEXCOUNT', 'key', '[a', '[b'] + ); + }); - testUtils.testWithClient('client.zLexCount', async client => { - assert.equal( - await client.zLexCount('key', '[a', '[b'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zLexCount', async client => { + assert.equal( + await client.zLexCount('key', '[a', '[b'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZLEXCOUNT.ts b/packages/client/lib/commands/ZLEXCOUNT.ts index e2fbcdbb42b..26c9e0d70ac 100644 --- a/packages/client/lib/commands/ZLEXCOUNT.ts +++ b/packages/client/lib/commands/ZLEXCOUNT.ts @@ -1,20 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument, - max: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + min: RedisArgument, + max: RedisArgument + ) { return [ - 'ZLEXCOUNT', - key, - min, - max + 'ZLEXCOUNT', + key, + min, + max ]; -} - -export declare function transformReply(): number; + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZMPOP.spec.ts b/packages/client/lib/commands/ZMPOP.spec.ts index 9a0c9676c20..d99cceb41ca 100644 --- a/packages/client/lib/commands/ZMPOP.spec.ts +++ b/packages/client/lib/commands/ZMPOP.spec.ts @@ -1,32 +1,32 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZMPOP'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZMPOP'; -describe('ZMPOP', () => { - testUtils.isVersionGreaterThanHook([7]); +// describe('ZMPOP', () => { +// testUtils.isVersionGreaterThanHook([7]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('key', 'MIN'), - ['ZMPOP', '1', 'key', 'MIN'] - ); - }); +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('key', 'MIN'), +// ['ZMPOP', '1', 'key', 'MIN'] +// ); +// }); - it('with score and count', () => { - assert.deepEqual( - transformArguments('key', 'MIN', { - COUNT: 2 - }), - ['ZMPOP', '1', 'key', 'MIN', 'COUNT', '2'] - ); - }); - }); +// it('with score and count', () => { +// assert.deepEqual( +// transformArguments('key', 'MIN', { +// COUNT: 2 +// }), +// ['ZMPOP', '1', 'key', 'MIN', 'COUNT', '2'] +// ); +// }); +// }); - testUtils.testWithClient('client.zmPop', async client => { - assert.deepEqual( - await client.zmPop('key', 'MIN'), - null - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zmPop', async client => { +// assert.deepEqual( +// await client.zmPop('key', 'MIN'), +// null +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZMPOP.ts b/packages/client/lib/commands/ZMPOP.ts index 0baa46bbf0b..ae704ab2863 100644 --- a/packages/client/lib/commands/ZMPOP.ts +++ b/packages/client/lib/commands/ZMPOP.ts @@ -1,34 +1,61 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { SortedSetSide, transformSortedSetMemberReply, transformZMPopArguments, ZMember, ZMPopOptions } from './generic-transformers'; +// import { NullReply, TuplesReply, BlobStringReply, DoubleReply, ArrayReply, Resp2Reply, Command, RedisArgument } from '../RESP/types'; +// import { pushVariadicArgument, RedisVariadicArgument, SortedSetSide } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +// export interface ZMPopOptions { +// COUNT?: number; +// } -export function transformArguments( - keys: RedisCommandArgument | Array, - side: SortedSetSide, - options?: ZMPopOptions -): RedisCommandArguments { - return transformZMPopArguments( - ['ZMPOP'], - keys, - side, - options - ); -} +// export type ZMPopRawReply = NullReply | TuplesReply<[ +// key: BlobStringReply, +// elements: ArrayReply> +// ]>; -type ZMPopRawReply = null | [ - key: string, - elements: Array<[RedisCommandArgument, RedisCommandArgument]> -]; +// export function pushZMPopArguments( +// args: Array, +// keys: RedisVariadicArgument, +// side: SortedSetSide, +// options: ZMPopOptions +// ) -type ZMPopReply = null | { - key: string, - elements: Array -}; +// export default { +// FIRST_KEY_INDEX: 2, +// IS_READ_ONLY: false, +// transformArguments( +// keys: RedisVariadicArgument, +// side: SortedSetSide, +// options?: ZMPopOptions +// ) { +// const args = pushVariadicArgument(['ZMPOP'], keys); -export function transformReply(reply: ZMPopRawReply): ZMPopReply { - return reply === null ? null : { - key: reply[0], - elements: reply[1].map(transformSortedSetMemberReply) - }; -} +// args.push(side); + +// if (options?.COUNT) { +// args.push('COUNT', options.COUNT.toString()); +// } + +// return args; +// }, +// transformReply: { +// 2: (reply: Resp2Reply) => { +// return reply === null ? null : { +// key: reply[0], +// elements: reply[1].map(([member, score]) => ({ +// member, +// score: Number(score) +// })) +// }; +// }, +// 3: (reply: ZMPopRawReply) => { +// return reply === null ? null : { +// key: reply[0], +// elements: reply[1].map(([member, score]) => ({ +// member, +// score +// })) +// }; +// }, +// } +// } as const satisfies Command; diff --git a/packages/client/lib/commands/ZMSCORE.spec.ts b/packages/client/lib/commands/ZMSCORE.spec.ts index 228c8e9d6f6..96cea5d5f78 100644 --- a/packages/client/lib/commands/ZMSCORE.spec.ts +++ b/packages/client/lib/commands/ZMSCORE.spec.ts @@ -1,30 +1,33 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZMSCORE'; +import ZMSCORE from './ZMSCORE'; describe('ZMSCORE', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['ZMSCORE', 'key', 'member'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ZMSCORE.transformArguments('key', 'member'), + ['ZMSCORE', 'key', 'member'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['ZMSCORE', 'key', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + ZMSCORE.transformArguments('key', ['1', '2']), + ['ZMSCORE', 'key', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.zmScore', async client => { - assert.deepEqual( - await client.zmScore('key', 'member'), - [null] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zmScore', async client => { + assert.deepEqual( + await client.zmScore('key', 'member'), + [null] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZMSCORE.ts b/packages/client/lib/commands/ZMSCORE.ts index 6c8c9dace31..f5a8fac0d7b 100644 --- a/packages/client/lib/commands/ZMSCORE.ts +++ b/packages/client/lib/commands/ZMSCORE.ts @@ -1,15 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, ArrayReply, NullReply, BlobStringReply, DoubleReply, Command } from '../RESP/types'; +import { pushVariadicArguments, RedisVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['ZMSCORE', key], member); -} - -export { transformNumberInfinityNullArrayReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + member: RedisVariadicArgument + ) { + return pushVariadicArguments(['ZMSCORE', key], member); + }, + transformReply: { + 2: (reply: ArrayReply) => { + return reply.map(score => score === null ? null : Number(score)); + }, + 3: undefined as unknown as () => ArrayReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZPOPMAX.spec.ts b/packages/client/lib/commands/ZPOPMAX.spec.ts index 18fba23a3e9..46b7cf1df84 100644 --- a/packages/client/lib/commands/ZPOPMAX.spec.ts +++ b/packages/client/lib/commands/ZPOPMAX.spec.ts @@ -1,41 +1,41 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './ZPOPMAX'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments, transformReply } from './ZPOPMAX'; -describe('ZPOPMAX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['ZPOPMAX', 'key'] - ); - }); +// describe('ZPOPMAX', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key'), +// ['ZPOPMAX', 'key'] +// ); +// }); - it('transformReply', () => { - assert.deepEqual( - transformReply(['value', '1']), - { - value: 'value', - score: 1 - } - ); - }); +// it('transformReply', () => { +// assert.deepEqual( +// transformReply(['value', '1']), +// { +// value: 'value', +// score: 1 +// } +// ); +// }); - describe('client.zPopMax', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.zPopMax('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); +// describe('client.zPopMax', () => { +// testUtils.testWithClient('null', async client => { +// assert.equal( +// await client.zPopMax('key'), +// null +// ); +// }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('member', async client => { - const member = { score: 1, value: 'value' }, - [, zPopMaxReply] = await Promise.all([ - client.zAdd('key', member), - client.zPopMax('key') - ]); +// testUtils.testWithClient('member', async client => { +// const member = { score: 1, value: 'value' }, +// [, zPopMaxReply] = await Promise.all([ +// client.zAdd('key', member), +// client.zPopMax('key') +// ]); - assert.deepEqual(zPopMaxReply, member); - }, GLOBAL.SERVERS.OPEN); - }); -}); +// assert.deepEqual(zPopMaxReply, member); +// }, GLOBAL.SERVERS.OPEN); +// }); +// }); diff --git a/packages/client/lib/commands/ZPOPMAX.ts b/packages/client/lib/commands/ZPOPMAX.ts index 811166a690c..959c3b927ba 100644 --- a/packages/client/lib/commands/ZPOPMAX.ts +++ b/packages/client/lib/commands/ZPOPMAX.ts @@ -1,12 +1,12 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return [ - 'ZPOPMAX', - key - ]; -} +// export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +// return [ +// 'ZPOPMAX', +// key +// ]; +// } -export { transformSortedSetMemberNullReply as transformReply } from './generic-transformers'; +// export { transformSortedSetMemberNullReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts index b282d0d3199..1214c8b2c29 100644 --- a/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZPOPMAX_COUNT'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZPOPMAX_COUNT'; -describe('ZPOPMAX COUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['ZPOPMAX', 'key', '1'] - ); - }); +// describe('ZPOPMAX COUNT', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 1), +// ['ZPOPMAX', 'key', '1'] +// ); +// }); - testUtils.testWithClient('client.zPopMaxCount', async client => { - assert.deepEqual( - await client.zPopMaxCount('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zPopMaxCount', async client => { +// assert.deepEqual( +// await client.zPopMaxCount('key', 1), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZPOPMAX_COUNT.ts b/packages/client/lib/commands/ZPOPMAX_COUNT.ts index 875bcfb9147..881f5472e68 100644 --- a/packages/client/lib/commands/ZPOPMAX_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMAX_COUNT.ts @@ -1,16 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformZPopMaxArguments } from './ZPOPMAX'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { transformArguments as transformZPopMaxArguments } from './ZPOPMAX'; -export { FIRST_KEY_INDEX } from './ZPOPMAX'; +// export { FIRST_KEY_INDEX } from './ZPOPMAX'; -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformZPopMaxArguments(key), - count.toString() - ]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// count: number +// ): RedisCommandArguments { +// return [ +// ...transformZPopMaxArguments(key), +// count.toString() +// ]; +// } -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +// export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZPOPMIN.spec.ts b/packages/client/lib/commands/ZPOPMIN.spec.ts index 624b7054404..575149632cc 100644 --- a/packages/client/lib/commands/ZPOPMIN.spec.ts +++ b/packages/client/lib/commands/ZPOPMIN.spec.ts @@ -1,41 +1,41 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './ZPOPMIN'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments, transformReply } from './ZPOPMIN'; -describe('ZPOPMIN', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['ZPOPMIN', 'key'] - ); - }); +// describe('ZPOPMIN', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key'), +// ['ZPOPMIN', 'key'] +// ); +// }); - it('transformReply', () => { - assert.deepEqual( - transformReply(['value', '1']), - { - value: 'value', - score: 1 - } - ); - }); +// it('transformReply', () => { +// assert.deepEqual( +// transformReply(['value', '1']), +// { +// value: 'value', +// score: 1 +// } +// ); +// }); - describe('client.zPopMin', () => { - testUtils.testWithClient('null', async client => { - assert.equal( - await client.zPopMin('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); +// describe('client.zPopMin', () => { +// testUtils.testWithClient('null', async client => { +// assert.equal( +// await client.zPopMin('key'), +// null +// ); +// }, GLOBAL.SERVERS.OPEN); - testUtils.testWithClient('member', async client => { - const member = { score: 1, value: 'value' }, - [, zPopMinReply] = await Promise.all([ - client.zAdd('key', member), - client.zPopMin('key') - ]); +// testUtils.testWithClient('member', async client => { +// const member = { score: 1, value: 'value' }, +// [, zPopMinReply] = await Promise.all([ +// client.zAdd('key', member), +// client.zPopMin('key') +// ]); - assert.deepEqual(zPopMinReply, member); - }, GLOBAL.SERVERS.OPEN); - }); -}); +// assert.deepEqual(zPopMinReply, member); +// }, GLOBAL.SERVERS.OPEN); +// }); +// }); diff --git a/packages/client/lib/commands/ZPOPMIN.ts b/packages/client/lib/commands/ZPOPMIN.ts index 053ffd2d2ce..dea8948b8bc 100644 --- a/packages/client/lib/commands/ZPOPMIN.ts +++ b/packages/client/lib/commands/ZPOPMIN.ts @@ -1,12 +1,12 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { - return [ - 'ZPOPMIN', - key - ]; -} +// export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +// return [ +// 'ZPOPMIN', +// key +// ]; +// } -export { transformSortedSetMemberNullReply as transformReply } from './generic-transformers'; +// export { transformSortedSetMemberNullReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts index 6d40002ab72..3d199c15384 100644 --- a/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZPOPMIN_COUNT'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZPOPMIN_COUNT'; -describe('ZPOPMIN COUNT', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['ZPOPMIN', 'key', '1'] - ); - }); +// describe('ZPOPMIN COUNT', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 1), +// ['ZPOPMIN', 'key', '1'] +// ); +// }); - testUtils.testWithClient('client.zPopMinCount', async client => { - assert.deepEqual( - await client.zPopMinCount('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zPopMinCount', async client => { +// assert.deepEqual( +// await client.zPopMinCount('key', 1), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZPOPMIN_COUNT.ts b/packages/client/lib/commands/ZPOPMIN_COUNT.ts index 54125ade0ac..385043be658 100644 --- a/packages/client/lib/commands/ZPOPMIN_COUNT.ts +++ b/packages/client/lib/commands/ZPOPMIN_COUNT.ts @@ -1,16 +1,16 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformZPopMinArguments } from './ZPOPMIN'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { transformArguments as transformZPopMinArguments } from './ZPOPMIN'; -export { FIRST_KEY_INDEX } from './ZPOPMIN'; +// export { FIRST_KEY_INDEX } from './ZPOPMIN'; -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformZPopMinArguments(key), - count.toString() - ]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// count: number +// ): RedisCommandArguments { +// return [ +// ...transformZPopMinArguments(key), +// count.toString() +// ]; +// } -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +// export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZRANDMEMBER.spec.ts b/packages/client/lib/commands/ZRANDMEMBER.spec.ts index c57d26f830e..5249399c427 100644 --- a/packages/client/lib/commands/ZRANDMEMBER.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER.spec.ts @@ -1,21 +1,24 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANDMEMBER'; +import ZRANDMEMBER from './ZRANDMEMBER'; describe('ZRANDMEMBER', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['ZRANDMEMBER', 'key'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZRANDMEMBER.transformArguments('key'), + ['ZRANDMEMBER', 'key'] + ); + }); - testUtils.testWithClient('client.zRandMember', async client => { - assert.equal( - await client.zRandMember('key'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRandMember', async client => { + assert.equal( + await client.zRandMember('key'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANDMEMBER.ts b/packages/client/lib/commands/ZRANDMEMBER.ts index 00420872c0c..449eb281c66 100644 --- a/packages/client/lib/commands/ZRANDMEMBER.ts +++ b/packages/client/lib/commands/ZRANDMEMBER.ts @@ -1,11 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments(key: RedisCommandArgument): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument) { return ['ZRANDMEMBER', key]; -} - -export declare function transformReply(): RedisCommandArgument | null; + }, + transformReply: undefined as unknown as () => BlobStringReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts index 10db0727b23..f108eb91ba8 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT.spec.ts @@ -1,21 +1,24 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANDMEMBER_COUNT'; +import ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; describe('ZRANDMEMBER COUNT', () => { - testUtils.isVersionGreaterThanHook([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['ZRANDMEMBER', 'key', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZRANDMEMBER_COUNT.transformArguments('key', 1), + ['ZRANDMEMBER', 'key', '1'] + ); + }); - testUtils.testWithClient('client.zRandMemberCount', async client => { - assert.deepEqual( - await client.zRandMemberCount('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRandMemberCount', async client => { + assert.deepEqual( + await client.zRandMemberCount('key', 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts index 3aa91902c62..89b921f007a 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT.ts @@ -1,16 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformArguments as transformZRandMemberArguments } from './ZRANDMEMBER'; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import ZRANDMEMBER from './ZRANDMEMBER'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER'; - -export function transformArguments( - key: RedisCommandArgument, - count: number -): RedisCommandArguments { - return [ - ...transformZRandMemberArguments(key), - count.toString() - ]; -} - -export declare function transformReply(): Array; +export default { + FIRST_KEY_INDEX: ZRANDMEMBER.FIRST_KEY_INDEX, + IS_READ_ONLY: ZRANDMEMBER.IS_READ_ONLY, + transformArguments(key: RedisArgument, count: number) { + const args = ZRANDMEMBER.transformArguments(key); + args.push(count.toString()); + return args; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts index 5b5ec1f500f..7ea438e1df3 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.spec.ts @@ -1,21 +1,24 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANDMEMBER_COUNT_WITHSCORES'; +import ZRANDMEMBER_COUNT_WITHSCORES from './ZRANDMEMBER_COUNT_WITHSCORES'; describe('ZRANDMEMBER COUNT WITHSCORES', () => { - testUtils.isVersionGreaterThanHook([6, 2, 5]); + testUtils.isVersionGreaterThanHook([6, 2, 5]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 1), - ['ZRANDMEMBER', 'key', '1', 'WITHSCORES'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZRANDMEMBER_COUNT_WITHSCORES.transformArguments('key', 1), + ['ZRANDMEMBER', 'key', '1', 'WITHSCORES'] + ); + }); - testUtils.testWithClient('client.zRandMemberCountWithScores', async client => { - assert.deepEqual( - await client.zRandMemberCountWithScores('key', 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRandMemberCountWithScores', async client => { + assert.deepEqual( + await client.zRandMemberCountWithScores('key', 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts index cc9d2bc26ee..14c28d4b6c6 100644 --- a/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANDMEMBER_COUNT_WITHSCORES.ts @@ -1,13 +1,14 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZRandMemberCountArguments } from './ZRANDMEMBER_COUNT'; +import { Command, RedisArgument } from '../RESP/types'; +import ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; +import { transformSortedSetReply } from './generic-transformers'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER_COUNT'; - -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZRandMemberCountArguments(...args), - 'WITHSCORES' - ]; -} - -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +export default { + FIRST_KEY_INDEX: ZRANDMEMBER_COUNT.FIRST_KEY_INDEX, + IS_READ_ONLY: ZRANDMEMBER_COUNT.IS_READ_ONLY, + transformArguments(key: RedisArgument, count: number) { + const args = ZRANDMEMBER_COUNT.transformArguments(key, count); + args.push('WITHSCORES'); + return args; + }, + transformReply: transformSortedSetReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGE.spec.ts b/packages/client/lib/commands/ZRANGE.spec.ts index a280aff0033..f147cfaa1b1 100644 --- a/packages/client/lib/commands/ZRANGE.spec.ts +++ b/packages/client/lib/commands/ZRANGE.spec.ts @@ -1,74 +1,77 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANGE'; +import ZRANGE from './ZRANGE'; describe('ZRANGE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('src', 0, 1), - ['ZRANGE', 'src', '0', '1'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1), + ['ZRANGE', 'src', '0', '1'] + ); + }); - it('with BYSCORE', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - BY: 'SCORE' - }), - ['ZRANGE', 'src', '0', '1', 'BYSCORE'] - ); - }); + it('with BYSCORE', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1, { + BY: 'SCORE' + }), + ['ZRANGE', 'src', '0', '1', 'BYSCORE'] + ); + }); - it('with BYLEX', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - BY: 'LEX' - }), - ['ZRANGE', 'src', '0', '1', 'BYLEX'] - ); - }); + it('with BYLEX', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1, { + BY: 'LEX' + }), + ['ZRANGE', 'src', '0', '1', 'BYLEX'] + ); + }); - it('with REV', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - REV: true - }), - ['ZRANGE', 'src', '0', '1', 'REV'] - ); - }); + it('with REV', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1, { + REV: true + }), + ['ZRANGE', 'src', '0', '1', 'REV'] + ); + }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGE', 'src', '0', '1', 'LIMIT', '0', '1'] - ); - }); + it('with LIMIT', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1, { + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGE', 'src', '0', '1', 'LIMIT', '0', '1'] + ); + }); - it('with BY & REV & LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - BY: 'SCORE', - REV: true, - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1'] - ); - }); + it('with BY & REV & LIMIT', () => { + assert.deepEqual( + ZRANGE.transformArguments('src', 0, 1, { + BY: 'SCORE', + REV: true, + LIMIT: { + offset: 0, + count: 1 + } + }), + ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1'] + ); }); + }); - testUtils.testWithClient('client.zRange', async client => { - assert.deepEqual( - await client.zRange('src', 0, 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRange', async client => { + assert.deepEqual( + await client.zRange('src', 0, 1), + [] + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANGE.ts b/packages/client/lib/commands/ZRANGE.ts index 83f09aaa1b0..557044b67da 100644 --- a/packages/client/lib/commands/ZRANGE.ts +++ b/packages/client/lib/commands/ZRANGE.ts @@ -1,51 +1,54 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -interface ZRangeOptions { - BY?: 'SCORE' | 'LEX'; - REV?: true; - LIMIT?: { - offset: number; - count: number; - }; +import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { transformStringDoubleArgument } from './generic-transformers'; + +export interface ZRangeOptions { + BY?: 'SCORE' | 'LEX'; + REV?: boolean; + LIMIT?: { + offset: number; + count: number; + }; } -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument | number, - max: RedisCommandArgument | number, +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + min: RedisArgument | number, + max: RedisArgument | number, options?: ZRangeOptions -): RedisCommandArguments { + ) { const args = [ - 'ZRANGE', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) + 'ZRANGE', + key, + transformStringDoubleArgument(min), + transformStringDoubleArgument(max) ]; switch (options?.BY) { - case 'SCORE': - args.push('BYSCORE'); - break; + case 'SCORE': + args.push('BYSCORE'); + break; - case 'LEX': - args.push('BYLEX'); - break; + case 'LEX': + args.push('BYLEX'); + break; } if (options?.REV) { - args.push('REV'); + args.push('REV'); } if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); + args.push( + 'LIMIT', + options.LIMIT.offset.toString(), + options.LIMIT.count.toString() + ); } return args; -} - -export declare function transformReply(): Array; + }, + transformReply: undefined as unknown as () => ArrayReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZRANGEBYLEX.spec.ts b/packages/client/lib/commands/ZRANGEBYLEX.spec.ts index fe7b7d5a16e..40b779eac2c 100644 --- a/packages/client/lib/commands/ZRANGEBYLEX.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.spec.ts @@ -1,33 +1,33 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANGEBYLEX'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZRANGEBYLEX'; -describe('ZRANGEBYLEX', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('src', '-', '+'), - ['ZRANGEBYLEX', 'src', '-', '+'] - ); - }); +// describe('ZRANGEBYLEX', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('src', '-', '+'), +// ['ZRANGEBYLEX', 'src', '-', '+'] +// ); +// }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('src', '-', '+', { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGEBYLEX', 'src', '-', '+', 'LIMIT', '0', '1'] - ); - }); - }); +// it('with LIMIT', () => { +// assert.deepEqual( +// transformArguments('src', '-', '+', { +// LIMIT: { +// offset: 0, +// count: 1 +// } +// }), +// ['ZRANGEBYLEX', 'src', '-', '+', 'LIMIT', '0', '1'] +// ); +// }); +// }); - testUtils.testWithClient('client.zRangeByLex', async client => { - assert.deepEqual( - await client.zRangeByLex('src', '-', '+'), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zRangeByLex', async client => { +// assert.deepEqual( +// await client.zRangeByLex('src', '-', '+'), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZRANGEBYLEX.ts b/packages/client/lib/commands/ZRANGEBYLEX.ts index d6e621a562f..d06debb008b 100644 --- a/packages/client/lib/commands/ZRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZRANGEBYLEX.ts @@ -1,35 +1,35 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { transformStringDoubleArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export interface ZRangeByLexOptions { - LIMIT?: { - offset: number; - count: number; - }; -} +// export interface ZRangeByLexOptions { +// LIMIT?: { +// offset: number; +// count: number; +// }; +// } -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument, - max: RedisCommandArgument, - options?: ZRangeByLexOptions -): RedisCommandArguments { - const args = [ - 'ZRANGEBYLEX', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) - ]; +// export function transformArguments( +// key: RedisCommandArgument, +// min: RedisCommandArgument, +// max: RedisCommandArgument, +// options?: ZRangeByLexOptions +// ): RedisCommandArguments { +// const args = [ +// 'ZRANGEBYLEX', +// key, +// transformStringDoubleArgument(min), +// transformStringDoubleArgument(max) +// ]; - if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); - } +// if (options?.LIMIT) { +// args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); +// } - return args; -} +// return args; +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts b/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts index a3484326306..1d064c37c12 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.spec.ts @@ -1,33 +1,33 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANGEBYSCORE'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZRANGEBYSCORE'; -describe('ZRANGEBYSCORE', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('src', 0, 1), - ['ZRANGEBYSCORE', 'src', '0', '1'] - ); - }); +// describe('ZRANGEBYSCORE', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('src', 0, 1), +// ['ZRANGEBYSCORE', 'src', '0', '1'] +// ); +// }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1'] - ); - }); - }); +// it('with LIMIT', () => { +// assert.deepEqual( +// transformArguments('src', 0, 1, { +// LIMIT: { +// offset: 0, +// count: 1 +// } +// }), +// ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1'] +// ); +// }); +// }); - testUtils.testWithClient('client.zRangeByScore', async client => { - assert.deepEqual( - await client.zRangeByScore('src', 0, 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zRangeByScore', async client => { +// assert.deepEqual( +// await client.zRangeByScore('src', 0, 1), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZRANGEBYSCORE.ts b/packages/client/lib/commands/ZRANGEBYSCORE.ts index 5ab7d7ac727..562dd6b5d0d 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE.ts @@ -1,35 +1,35 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { transformStringDoubleArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -export interface ZRangeByScoreOptions { - LIMIT?: { - offset: number; - count: number; - }; -} +// export interface ZRangeByScoreOptions { +// LIMIT?: { +// offset: number; +// count: number; +// }; +// } -export function transformArguments( - key: RedisCommandArgument, - min: string | number, - max: string | number, - options?: ZRangeByScoreOptions -): RedisCommandArguments { - const args = [ - 'ZRANGEBYSCORE', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) - ]; +// export function transformArguments( +// key: RedisCommandArgument, +// min: string | number, +// max: string | number, +// options?: ZRangeByScoreOptions +// ): RedisCommandArguments { +// const args = [ +// 'ZRANGEBYSCORE', +// key, +// transformStringDoubleArgument(min), +// transformStringDoubleArgument(max) +// ]; - if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); - } +// if (options?.LIMIT) { +// args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); +// } - return args; -} +// return args; +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts index 3552d3e2535..2a90a6af3f7 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.spec.ts @@ -1,33 +1,33 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANGEBYSCORE_WITHSCORES'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZRANGEBYSCORE_WITHSCORES'; -describe('ZRANGEBYSCORE WITHSCORES', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('src', 0, 1), - ['ZRANGEBYSCORE', 'src', '0', '1', 'WITHSCORES'] - ); - }); +// describe('ZRANGEBYSCORE WITHSCORES', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('src', 0, 1), +// ['ZRANGEBYSCORE', 'src', '0', '1', 'WITHSCORES'] +// ); +// }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] - ); - }); - }); +// it('with LIMIT', () => { +// assert.deepEqual( +// transformArguments('src', 0, 1, { +// LIMIT: { +// offset: 0, +// count: 1 +// } +// }), +// ['ZRANGEBYSCORE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] +// ); +// }); +// }); - testUtils.testWithClient('client.zRangeByScoreWithScores', async client => { - assert.deepEqual( - await client.zRangeByScoreWithScores('src', 0, 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zRangeByScoreWithScores', async client => { +// assert.deepEqual( +// await client.zRangeByScoreWithScores('src', 0, 1), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts index c7266f1c062..1cd2df10f0a 100644 --- a/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGEBYSCORE_WITHSCORES.ts @@ -1,18 +1,18 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ZRangeByScoreOptions, transformArguments as transformZRangeByScoreArguments } from './ZRANGEBYSCORE'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { ZRangeByScoreOptions, transformArguments as transformZRangeByScoreArguments } from './ZRANGEBYSCORE'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGEBYSCORE'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGEBYSCORE'; -export function transformArguments( - key: RedisCommandArgument, - min: string | number, - max: string | number, - options?: ZRangeByScoreOptions -): RedisCommandArguments { - return [ - ...transformZRangeByScoreArguments(key, min, max, options), - 'WITHSCORES' - ]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// min: string | number, +// max: string | number, +// options?: ZRangeByScoreOptions +// ): RedisCommandArguments { +// return [ +// ...transformZRangeByScoreArguments(key, min, max, options), +// 'WITHSCORES' +// ]; +// } -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +// export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZRANGESTORE.spec.ts b/packages/client/lib/commands/ZRANGESTORE.spec.ts index 7af253e539f..c617f38e9ae 100644 --- a/packages/client/lib/commands/ZRANGESTORE.spec.ts +++ b/packages/client/lib/commands/ZRANGESTORE.spec.ts @@ -1,92 +1,92 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './ZRANGESTORE'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments, transformReply } from './ZRANGESTORE'; -describe('ZRANGESTORE', () => { - testUtils.isVersionGreaterThanHook([6, 2]); +// describe('ZRANGESTORE', () => { +// testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1), - ['ZRANGESTORE', 'dst', 'src', '0', '1'] - ); - }); +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('dst', 'src', 0, 1), +// ['ZRANGESTORE', 'dst', 'src', '0', '1'] +// ); +// }); - it('with BYSCORE', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1, { - BY: 'SCORE' - }), - ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYSCORE'] - ); - }); +// it('with BYSCORE', () => { +// assert.deepEqual( +// transformArguments('dst', 'src', 0, 1, { +// BY: 'SCORE' +// }), +// ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYSCORE'] +// ); +// }); - it('with BYLEX', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1, { - BY: 'LEX' - }), - ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYLEX'] - ); - }); +// it('with BYLEX', () => { +// assert.deepEqual( +// transformArguments('dst', 'src', 0, 1, { +// BY: 'LEX' +// }), +// ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYLEX'] +// ); +// }); - it('with REV', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1, { - REV: true - }), - ['ZRANGESTORE', 'dst', 'src', '0', '1', 'REV'] - ); - }); +// it('with REV', () => { +// assert.deepEqual( +// transformArguments('dst', 'src', 0, 1, { +// REV: true +// }), +// ['ZRANGESTORE', 'dst', 'src', '0', '1', 'REV'] +// ); +// }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1, { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGESTORE', 'dst', 'src', '0', '1', 'LIMIT', '0', '1'] - ); - }); +// it('with LIMIT', () => { +// assert.deepEqual( +// transformArguments('dst', 'src', 0, 1, { +// LIMIT: { +// offset: 0, +// count: 1 +// } +// }), +// ['ZRANGESTORE', 'dst', 'src', '0', '1', 'LIMIT', '0', '1'] +// ); +// }); - it('with BY & REV & LIMIT', () => { - assert.deepEqual( - transformArguments('dst', 'src', 0, 1, { - BY: 'SCORE', - REV: true, - LIMIT: { - offset: 0, - count: 1 - }, - WITHSCORES: true - }), - ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1', 'WITHSCORES'] - ); - }); - }); +// it('with BY & REV & LIMIT', () => { +// assert.deepEqual( +// transformArguments('dst', 'src', 0, 1, { +// BY: 'SCORE', +// REV: true, +// LIMIT: { +// offset: 0, +// count: 1 +// }, +// WITHSCORES: true +// }), +// ['ZRANGESTORE', 'dst', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1', 'WITHSCORES'] +// ); +// }); +// }); - describe('transformReply', () => { - it('should throw TypeError when reply is not a number', () => { - assert.throws( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - () => (transformReply as any)([]), - TypeError - ); - }); - }); +// describe('transformReply', () => { +// it('should throw TypeError when reply is not a number', () => { +// assert.throws( +// // eslint-disable-next-line @typescript-eslint/no-explicit-any +// () => (transformReply as any)([]), +// TypeError +// ); +// }); +// }); - testUtils.testWithClient('client.zRangeStore', async client => { - await client.zAdd('src', { - score: 0.5, - value: 'value' - }); +// testUtils.testWithClient('client.zRangeStore', async client => { +// await client.zAdd('src', { +// score: 0.5, +// value: 'value' +// }); - assert.equal( - await client.zRangeStore('dst', 'src', 0, 1), - 1 - ); - }, GLOBAL.SERVERS.OPEN); -}); +// assert.equal( +// await client.zRangeStore('dst', 'src', 0, 1), +// 1 +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZRANGESTORE.ts b/packages/client/lib/commands/ZRANGESTORE.ts index 28067ceabe0..96fd8302de4 100644 --- a/packages/client/lib/commands/ZRANGESTORE.ts +++ b/packages/client/lib/commands/ZRANGESTORE.ts @@ -1,62 +1,62 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; - -export const FIRST_KEY_INDEX = 1; - -interface ZRangeStoreOptions { - BY?: 'SCORE' | 'LEX'; - REV?: true; - LIMIT?: { - offset: number; - count: number; - }; - WITHSCORES?: true; -} - -export function transformArguments( - dst: RedisCommandArgument, - src: RedisCommandArgument, - min: RedisCommandArgument | number, - max: RedisCommandArgument | number, - options?: ZRangeStoreOptions -): RedisCommandArguments { - const args = [ - 'ZRANGESTORE', - dst, - src, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) - ]; - - switch (options?.BY) { - case 'SCORE': - args.push('BYSCORE'); - break; - - case 'LEX': - args.push('BYLEX'); - break; - } - - if (options?.REV) { - args.push('REV'); - } - - if (options?.LIMIT) { - args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); - } - - if (options?.WITHSCORES) { - args.push('WITHSCORES'); - } - - return args; -} - -export function transformReply(reply: number): number { - if (typeof reply !== 'number') { - throw new TypeError(`Upgrade to Redis 6.2.5 and up (https://github.com/redis/redis/pull/9089)`); - } - - return reply; -} +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { transformStringDoubleArgument } from './generic-transformers'; + +// export const FIRST_KEY_INDEX = 1; + +// interface ZRangeStoreOptions { +// BY?: 'SCORE' | 'LEX'; +// REV?: true; +// LIMIT?: { +// offset: number; +// count: number; +// }; +// WITHSCORES?: true; +// } + +// export function transformArguments( +// dst: RedisCommandArgument, +// src: RedisCommandArgument, +// min: RedisCommandArgument | number, +// max: RedisCommandArgument | number, +// options?: ZRangeStoreOptions +// ): RedisCommandArguments { +// const args = [ +// 'ZRANGESTORE', +// dst, +// src, +// transformStringDoubleArgument(min), +// transformStringDoubleArgument(max) +// ]; + +// switch (options?.BY) { +// case 'SCORE': +// args.push('BYSCORE'); +// break; + +// case 'LEX': +// args.push('BYLEX'); +// break; +// } + +// if (options?.REV) { +// args.push('REV'); +// } + +// if (options?.LIMIT) { +// args.push('LIMIT', options.LIMIT.offset.toString(), options.LIMIT.count.toString()); +// } + +// if (options?.WITHSCORES) { +// args.push('WITHSCORES'); +// } + +// return args; +// } + +// export function transformReply(reply: number): number { +// if (typeof reply !== 'number') { +// throw new TypeError(`Upgrade to Redis 6.2.5 and up (https://github.com/redis/redis/pull/9089)`); +// } + +// return reply; +// } diff --git a/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts index d9b07e19dda..0f106910f5f 100644 --- a/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZRANGE_WITHSCORES.spec.ts @@ -1,65 +1,65 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANGE_WITHSCORES'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZRANGE_WITHSCORES'; -describe('ZRANGE WITHSCORES', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments('src', 0, 1), - ['ZRANGE', 'src', '0', '1', 'WITHSCORES'] - ); - }); +// describe('ZRANGE WITHSCORES', () => { +// describe('transformArguments', () => { +// it('simple', () => { +// assert.deepEqual( +// transformArguments('src', 0, 1), +// ['ZRANGE', 'src', '0', '1', 'WITHSCORES'] +// ); +// }); - it('with BY', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - BY: 'SCORE' - }), - ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'WITHSCORES'] - ); - }); +// it('with BY', () => { +// assert.deepEqual( +// transformArguments('src', 0, 1, { +// BY: 'SCORE' +// }), +// ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'WITHSCORES'] +// ); +// }); - it('with REV', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - REV: true - }), - ['ZRANGE', 'src', '0', '1', 'REV', 'WITHSCORES'] - ); - }); +// it('with REV', () => { +// assert.deepEqual( +// transformArguments('src', 0, 1, { +// REV: true +// }), +// ['ZRANGE', 'src', '0', '1', 'REV', 'WITHSCORES'] +// ); +// }); - it('with LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] - ); - }); +// it('with LIMIT', () => { +// assert.deepEqual( +// transformArguments('src', 0, 1, { +// LIMIT: { +// offset: 0, +// count: 1 +// } +// }), +// ['ZRANGE', 'src', '0', '1', 'LIMIT', '0', '1', 'WITHSCORES'] +// ); +// }); - it('with BY & REV & LIMIT', () => { - assert.deepEqual( - transformArguments('src', 0, 1, { - BY: 'SCORE', - REV: true, - LIMIT: { - offset: 0, - count: 1 - } - }), - ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1', 'WITHSCORES'] - ); - }); - }); +// it('with BY & REV & LIMIT', () => { +// assert.deepEqual( +// transformArguments('src', 0, 1, { +// BY: 'SCORE', +// REV: true, +// LIMIT: { +// offset: 0, +// count: 1 +// } +// }), +// ['ZRANGE', 'src', '0', '1', 'BYSCORE', 'REV', 'LIMIT', '0', '1', 'WITHSCORES'] +// ); +// }); +// }); - testUtils.testWithClient('client.zRangeWithScores', async client => { - assert.deepEqual( - await client.zRangeWithScores('src', 0, 1), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zRangeWithScores', async client => { +// assert.deepEqual( +// await client.zRangeWithScores('src', 0, 1), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZRANGE_WITHSCORES.ts b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts index 23ea4d6337c..d05b856263c 100644 --- a/packages/client/lib/commands/ZRANGE_WITHSCORES.ts +++ b/packages/client/lib/commands/ZRANGE_WITHSCORES.ts @@ -1,13 +1,13 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZRangeArguments } from './ZRANGE'; +// import { RedisCommandArguments } from '.'; +// import { transformArguments as transformZRangeArguments } from './ZRANGE'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGE'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANGE'; -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZRangeArguments(...args), - 'WITHSCORES' - ]; -} +// export function transformArguments(...args: Parameters): RedisCommandArguments { +// return [ +// ...transformZRangeArguments(...args), +// 'WITHSCORES' +// ]; +// } -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +// export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/ZRANK.spec.ts b/packages/client/lib/commands/ZRANK.spec.ts index 0c81517a7d6..cd3d1725b0e 100644 --- a/packages/client/lib/commands/ZRANK.spec.ts +++ b/packages/client/lib/commands/ZRANK.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZRANK'; +import ZRANK from './ZRANK'; describe('ZRANK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['ZRANK', 'key', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZRANK.transformArguments('key', 'member'), + ['ZRANK', 'key', 'member'] + ); + }); - testUtils.testWithClient('client.zRank', async client => { - assert.equal( - await client.zRank('key', 'member'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRank', async client => { + assert.equal( + await client.zRank('key', 'member'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZRANK.ts b/packages/client/lib/commands/ZRANK.ts index 33439ea4b55..11184c0a28f 100644 --- a/packages/client/lib/commands/ZRANK.ts +++ b/packages/client/lib/commands/ZRANK.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { RedisArgument, NumberReply, NullReply, Command } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, member: RedisArgument) { return ['ZRANK', key, member]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZREM.spec.ts b/packages/client/lib/commands/ZREM.spec.ts index 3ac001708a0..ac52b171a8f 100644 --- a/packages/client/lib/commands/ZREM.spec.ts +++ b/packages/client/lib/commands/ZREM.spec.ts @@ -1,28 +1,31 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZREM'; +import ZREM from './ZREM'; describe('ZREM', () => { - describe('transformArguments', () => { - it('string', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['ZREM', 'key', 'member'] - ); - }); + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + ZREM.transformArguments('key', 'member'), + ['ZREM', 'key', 'member'] + ); + }); - it('array', () => { - assert.deepEqual( - transformArguments('key', ['1', '2']), - ['ZREM', 'key', '1', '2'] - ); - }); + it('array', () => { + assert.deepEqual( + ZREM.transformArguments('key', ['1', '2']), + ['ZREM', 'key', '1', '2'] + ); }); + }); - testUtils.testWithClient('client.zRem', async client => { - assert.equal( - await client.zRem('key', 'member'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRem', async client => { + assert.equal( + await client.zRem('key', 'member'), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZREM.ts b/packages/client/lib/commands/ZREM.ts index 7ab92c4a78f..54f55841fce 100644 --- a/packages/client/lib/commands/ZREM.ts +++ b/packages/client/lib/commands/ZREM.ts @@ -1,13 +1,14 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArguments } from './generic-transformers'; +import { RedisArgument, NumberReply, Command } from '../RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument | Array -): RedisCommandArguments { - return pushVerdictArguments(['ZREM', key], member); -} - -export declare function transformReply(): number; +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: false, + transformArguments( + key: RedisArgument, + member: RedisVariadicArgument + ) { + return pushVariadicArguments(['ZREM', key], member); + }, + transformReply: undefined as unknown as () => NumberReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts index b59c9e9f3b0..5b9f74fbe42 100644 --- a/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYLEX.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZREMRANGEBYLEX'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZREMRANGEBYLEX'; -describe('ZREMRANGEBYLEX', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '[a', '[b'), - ['ZREMRANGEBYLEX', 'key', '[a', '[b'] - ); - }); +// describe('ZREMRANGEBYLEX', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', '[a', '[b'), +// ['ZREMRANGEBYLEX', 'key', '[a', '[b'] +// ); +// }); - testUtils.testWithClient('client.zRemRangeByLex', async client => { - assert.equal( - await client.zRemRangeByLex('key', '[a', '[b'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zRemRangeByLex', async client => { +// assert.equal( +// await client.zRemRangeByLex('key', '[a', '[b'), +// 0 +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZREMRANGEBYLEX.ts b/packages/client/lib/commands/ZREMRANGEBYLEX.ts index f1f3908f538..8c7adf0c91f 100644 --- a/packages/client/lib/commands/ZREMRANGEBYLEX.ts +++ b/packages/client/lib/commands/ZREMRANGEBYLEX.ts @@ -1,19 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { transformStringDoubleArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument | number, - max: RedisCommandArgument | number -): RedisCommandArguments { - return [ - 'ZREMRANGEBYLEX', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) - ]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// min: RedisCommandArgument | number, +// max: RedisCommandArgument | number +// ): RedisCommandArguments { +// return [ +// 'ZREMRANGEBYLEX', +// key, +// transformStringDoubleArgument(min), +// transformStringDoubleArgument(max) +// ]; +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts index c659dadb790..4dd660af742 100644 --- a/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYRANK.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZREMRANGEBYRANK'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZREMRANGEBYRANK'; -describe('ZREMRANGEBYRANK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 1), - ['ZREMRANGEBYRANK', 'key', '0', '1'] - ); - }); +// describe('ZREMRANGEBYRANK', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 0, 1), +// ['ZREMRANGEBYRANK', 'key', '0', '1'] +// ); +// }); - testUtils.testWithClient('client.zRemRangeByRank', async client => { - assert.equal( - await client.zRemRangeByRank('key', 0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zRemRangeByRank', async client => { +// assert.equal( +// await client.zRemRangeByRank('key', 0, 1), +// 0 +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZREMRANGEBYRANK.ts b/packages/client/lib/commands/ZREMRANGEBYRANK.ts index c50d06e3bf6..bfb36cb6cbd 100644 --- a/packages/client/lib/commands/ZREMRANGEBYRANK.ts +++ b/packages/client/lib/commands/ZREMRANGEBYRANK.ts @@ -1,13 +1,13 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments( - key: RedisCommandArgument, - start: number, - stop: number -): RedisCommandArguments { - return ['ZREMRANGEBYRANK', key, start.toString(), stop.toString()]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// start: number, +// stop: number +// ): RedisCommandArguments { +// return ['ZREMRANGEBYRANK', key, start.toString(), stop.toString()]; +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts index 988fd7690c9..8d4bbf5286a 100644 --- a/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts +++ b/packages/client/lib/commands/ZREMRANGEBYSCORE.spec.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZREMRANGEBYSCORE'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZREMRANGEBYSCORE'; -describe('ZREMRANGEBYSCORE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 0, 1), - ['ZREMRANGEBYSCORE', 'key', '0', '1'] - ); - }); +// describe('ZREMRANGEBYSCORE', () => { +// it('transformArguments', () => { +// assert.deepEqual( +// transformArguments('key', 0, 1), +// ['ZREMRANGEBYSCORE', 'key', '0', '1'] +// ); +// }); - testUtils.testWithClient('client.zRemRangeByScore', async client => { - assert.equal( - await client.zRemRangeByScore('key', 0, 1), - 0 - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zRemRangeByScore', async client => { +// assert.equal( +// await client.zRemRangeByScore('key', 0, 1), +// 0 +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZREMRANGEBYSCORE.ts b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts index 12d1eff811e..802e42db8da 100644 --- a/packages/client/lib/commands/ZREMRANGEBYSCORE.ts +++ b/packages/client/lib/commands/ZREMRANGEBYSCORE.ts @@ -1,19 +1,19 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { transformStringNumberInfinityArgument } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { transformStringDoubleArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -export function transformArguments( - key: RedisCommandArgument, - min: RedisCommandArgument | number, - max: RedisCommandArgument | number, -): RedisCommandArguments { - return [ - 'ZREMRANGEBYSCORE', - key, - transformStringNumberInfinityArgument(min), - transformStringNumberInfinityArgument(max) - ]; -} +// export function transformArguments( +// key: RedisCommandArgument, +// min: RedisCommandArgument | number, +// max: RedisCommandArgument | number, +// ): RedisCommandArguments { +// return [ +// 'ZREMRANGEBYSCORE', +// key, +// transformStringDoubleArgument(min), +// transformStringDoubleArgument(max) +// ]; +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ZREVRANK.spec.ts b/packages/client/lib/commands/ZREVRANK.spec.ts index d9fef0d70a4..f7e104bca3b 100644 --- a/packages/client/lib/commands/ZREVRANK.spec.ts +++ b/packages/client/lib/commands/ZREVRANK.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZREVRANK'; +import ZREVRANK from './ZREVRANK'; describe('ZREVRANK', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['ZREVRANK', 'key', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZREVRANK.transformArguments('key', 'member'), + ['ZREVRANK', 'key', 'member'] + ); + }); - testUtils.testWithClient('client.zRevRank', async client => { - assert.equal( - await client.zRevRank('key', 'member'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zRevRank', async client => { + assert.equal( + await client.zRevRank('key', 'member'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZREVRANK.ts b/packages/client/lib/commands/ZREVRANK.ts index b88936c0c92..3bf52d21de5 100644 --- a/packages/client/lib/commands/ZREVRANK.ts +++ b/packages/client/lib/commands/ZREVRANK.ts @@ -1,14 +1,10 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { NumberReply, NullReply, Command, RedisArgument } from '../RESP/types'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, member: RedisArgument) { return ['ZREVRANK', key, member]; -} - -export declare function transformReply(): number | null; + }, + transformReply: undefined as unknown as () => NumberReply | NullReply +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZSCAN.spec.ts b/packages/client/lib/commands/ZSCAN.spec.ts index afa221a1ef3..f420fc0806a 100644 --- a/packages/client/lib/commands/ZSCAN.spec.ts +++ b/packages/client/lib/commands/ZSCAN.spec.ts @@ -1,77 +1,52 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments, transformReply } from './ZSCAN'; +import ZSCAN from './ZSCAN'; describe('ZSCAN', () => { - describe('transformArguments', () => { - it('cusror only', () => { - assert.deepEqual( - transformArguments('key', 0), - ['ZSCAN', 'key', '0'] - ); - }); - - it('with MATCH', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern' - }), - ['ZSCAN', 'key', '0', 'MATCH', 'pattern'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - COUNT: 1 - }), - ['ZSCAN', 'key', '0', 'COUNT', '1'] - ); - }); + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + ZSCAN.transformArguments('key', 0), + ['ZSCAN', 'key', '0'] + ); + }); - it('with MATCH & COUNT', () => { - assert.deepEqual( - transformArguments('key', 0, { - MATCH: 'pattern', - COUNT: 1 - }), - ['ZSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] - ); - }); + it('with MATCH', () => { + assert.deepEqual( + ZSCAN.transformArguments('key', 0, { + MATCH: 'pattern' + }), + ['ZSCAN', 'key', '0', 'MATCH', 'pattern'] + ); }); - describe('transformReply', () => { - it('without members', () => { - assert.deepEqual( - transformReply(['0', []]), - { - cursor: 0, - members: [] - } - ); - }); + it('with COUNT', () => { + assert.deepEqual( + ZSCAN.transformArguments('key', 0, { + COUNT: 1 + }), + ['ZSCAN', 'key', '0', 'COUNT', '1'] + ); + }); - it('with members', () => { - assert.deepEqual( - transformReply(['0', ['member', '-inf']]), - { - cursor: 0, - members: [{ - value: 'member', - score: -Infinity - }] - } - ); - }); + it('with MATCH & COUNT', () => { + assert.deepEqual( + ZSCAN.transformArguments('key', 0, { + MATCH: 'pattern', + COUNT: 1 + }), + ['ZSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1'] + ); }); + }); - testUtils.testWithClient('client.zScan', async client => { - assert.deepEqual( - await client.zScan('key', 0), - { - cursor: 0, - members: [] - } - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('zScan', async client => { + assert.deepEqual( + await client.zScan('key', 0), + { + cursor: 0, + members: [] + } + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/ZSCAN.ts b/packages/client/lib/commands/ZSCAN.ts index f6fa17c2d4e..50c9c5fa9cb 100644 --- a/packages/client/lib/commands/ZSCAN.ts +++ b/packages/client/lib/commands/ZSCAN.ts @@ -1,39 +1,35 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { ScanOptions, transformNumberInfinityReply, pushScanArguments, ZMember } from './generic-transformers'; +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; +import { ScanCommonOptions, pushScanArguments } from './SCAN'; +import { ZMember, transformDoubleReply } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - cursor: number, - options?: ScanOptions -): RedisCommandArguments { - return pushScanArguments([ - 'ZSCAN', - key - ], cursor, options); -} - -type ZScanRawReply = [RedisCommandArgument, Array]; - -interface ZScanReply { - cursor: number; - members: Array; +export interface HScanEntry { + field: BlobStringReply; + value: BlobStringReply; } -export function transformReply([cursor, rawMembers]: ZScanRawReply): ZScanReply { - const parsedMembers: Array = []; - for (let i = 0; i < rawMembers.length; i += 2) { - parsedMembers.push({ - value: rawMembers[i], - score: transformNumberInfinityReply(rawMembers[i + 1]) - }); +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + cursor: number, + options?: ScanCommonOptions + ) { + return pushScanArguments(['ZSCAN', key], cursor, options); + }, + transformReply([cursor, rawMembers]: [BlobStringReply, Array]) { + const members = []; + let i = 0; + while (i < rawMembers.length) { + members.push({ + value: rawMembers[i++], + score: transformDoubleReply(rawMembers[i++]) + } satisfies ZMember); } return { - cursor: Number(cursor), - members: parsedMembers + cursor: Number(cursor), + members }; -} + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZSCORE.spec.ts b/packages/client/lib/commands/ZSCORE.spec.ts index fe2a1c6a7c5..55c6d51cd05 100644 --- a/packages/client/lib/commands/ZSCORE.spec.ts +++ b/packages/client/lib/commands/ZSCORE.spec.ts @@ -1,19 +1,22 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZSCORE'; +import ZSCORE from './ZSCORE'; describe('ZSCORE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', 'member'), - ['ZSCORE', 'key', 'member'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + ZSCORE.transformArguments('key', 'member'), + ['ZSCORE', 'key', 'member'] + ); + }); - testUtils.testWithClient('client.zScore', async client => { - assert.equal( - await client.zScore('key', 'member'), - null - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testAll('zScore', async client => { + assert.equal( + await client.zScore('key', 'member'), + null + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); }); diff --git a/packages/client/lib/commands/ZSCORE.ts b/packages/client/lib/commands/ZSCORE.ts index 118abc10850..b62e6b53059 100644 --- a/packages/client/lib/commands/ZSCORE.ts +++ b/packages/client/lib/commands/ZSCORE.ts @@ -1,14 +1,15 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -export const FIRST_KEY_INDEX = 1; +import { DoubleReply, NullReply, Command, RedisArgument } from '../RESP/types'; +import { transformNullableDoubleReply } from './generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments( - key: RedisCommandArgument, - member: RedisCommandArgument -): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments(key: RedisArgument, member: RedisArgument) { return ['ZSCORE', key, member]; -} - -export { transformNumberInfinityNullReply as transformReply } from './generic-transformers'; + }, + transformReply: { + 2: transformNullableDoubleReply, + 3: undefined as unknown as () => DoubleReply | NullReply + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/ZUNION.spec.ts b/packages/client/lib/commands/ZUNION.spec.ts index c53498cbf65..b9882906047 100644 --- a/packages/client/lib/commands/ZUNION.spec.ts +++ b/packages/client/lib/commands/ZUNION.spec.ts @@ -1,48 +1,48 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZUNION'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZUNION'; -describe('ZUNION', () => { - testUtils.isVersionGreaterThanHook([6, 2]); +// describe('ZUNION', () => { +// testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('key'), - ['ZUNION', '1', 'key'] - ); - }); +// describe('transformArguments', () => { +// it('key (string)', () => { +// assert.deepEqual( +// transformArguments('key'), +// ['ZUNION', '1', 'key'] +// ); +// }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZUNION', '2', '1', '2'] - ); - }); +// it('keys (array)', () => { +// assert.deepEqual( +// transformArguments(['1', '2']), +// ['ZUNION', '2', '1', '2'] +// ); +// }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1] - }), - ['ZUNION', '1', 'key', 'WEIGHTS', '1'] - ); - }); +// it('with WEIGHTS', () => { +// assert.deepEqual( +// transformArguments('key', { +// WEIGHTS: [1] +// }), +// ['ZUNION', '1', 'key', 'WEIGHTS', '1'] +// ); +// }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - AGGREGATE: 'SUM' - }), - ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM'] - ); - }); - }); +// it('with AGGREGATE', () => { +// assert.deepEqual( +// transformArguments('key', { +// AGGREGATE: 'SUM' +// }), +// ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM'] +// ); +// }); +// }); - testUtils.testWithClient('client.zUnion', async client => { - assert.deepEqual( - await client.zUnion('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zUnion', async client => { +// assert.deepEqual( +// await client.zUnion('key'), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZUNION.ts b/packages/client/lib/commands/ZUNION.ts index f329348cc8b..537aecc8508 100644 --- a/packages/client/lib/commands/ZUNION.ts +++ b/packages/client/lib/commands/ZUNION.ts @@ -1,30 +1,30 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { pushVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 2; +// export const FIRST_KEY_INDEX = 2; -export const IS_READ_ONLY = true; +// export const IS_READ_ONLY = true; -interface ZUnionOptions { - WEIGHTS?: Array; - AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; -} +// interface ZUnionOptions { +// WEIGHTS?: Array; +// AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; +// } -export function transformArguments( - keys: Array | RedisCommandArgument, - options?: ZUnionOptions -): RedisCommandArguments { - const args = pushVerdictArgument(['ZUNION'], keys); +// export function transformArguments( +// keys: Array | RedisCommandArgument, +// options?: ZUnionOptions +// ): RedisCommandArguments { +// const args = pushVariadicArgument(['ZUNION'], keys); - if (options?.WEIGHTS) { - args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString())); - } +// if (options?.WEIGHTS) { +// args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString())); +// } - if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); - } +// if (options?.AGGREGATE) { +// args.push('AGGREGATE', options.AGGREGATE); +// } - return args; -} +// return args; +// } -export declare function transformReply(): Array; +// export declare function transformReply(): Array; diff --git a/packages/client/lib/commands/ZUNIONSTORE.spec.ts b/packages/client/lib/commands/ZUNIONSTORE.spec.ts index 8f11828b221..4458dcb648c 100644 --- a/packages/client/lib/commands/ZUNIONSTORE.spec.ts +++ b/packages/client/lib/commands/ZUNIONSTORE.spec.ts @@ -1,56 +1,56 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZUNIONSTORE'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZUNIONSTORE'; -describe('ZUNIONSTORE', () => { - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('destination', 'key'), - ['ZUNIONSTORE', 'destination', '1', 'key'] - ); - }); +// describe('ZUNIONSTORE', () => { +// describe('transformArguments', () => { +// it('key (string)', () => { +// assert.deepEqual( +// transformArguments('destination', 'key'), +// ['ZUNIONSTORE', 'destination', '1', 'key'] +// ); +// }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments('destination', ['1', '2']), - ['ZUNIONSTORE', 'destination', '2', '1', '2'] - ); - }); +// it('keys (array)', () => { +// assert.deepEqual( +// transformArguments('destination', ['1', '2']), +// ['ZUNIONSTORE', 'destination', '2', '1', '2'] +// ); +// }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - WEIGHTS: [1] - }), - ['ZUNIONSTORE', 'destination', '1', 'key', 'WEIGHTS', '1'] - ); - }); +// it('with WEIGHTS', () => { +// assert.deepEqual( +// transformArguments('destination', 'key', { +// WEIGHTS: [1] +// }), +// ['ZUNIONSTORE', 'destination', '1', 'key', 'WEIGHTS', '1'] +// ); +// }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - AGGREGATE: 'SUM' - }), - ['ZUNIONSTORE', 'destination', '1', 'key', 'AGGREGATE', 'SUM'] - ); - }); +// it('with AGGREGATE', () => { +// assert.deepEqual( +// transformArguments('destination', 'key', { +// AGGREGATE: 'SUM' +// }), +// ['ZUNIONSTORE', 'destination', '1', 'key', 'AGGREGATE', 'SUM'] +// ); +// }); - it('with WEIGHTS, AGGREGATE', () => { - assert.deepEqual( - transformArguments('destination', 'key', { - WEIGHTS: [1], - AGGREGATE: 'SUM' - }), - ['ZUNIONSTORE', 'destination', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM'] - ); - }); - }); +// it('with WEIGHTS, AGGREGATE', () => { +// assert.deepEqual( +// transformArguments('destination', 'key', { +// WEIGHTS: [1], +// AGGREGATE: 'SUM' +// }), +// ['ZUNIONSTORE', 'destination', '1', 'key', 'WEIGHTS', '1', 'AGGREGATE', 'SUM'] +// ); +// }); +// }); - testUtils.testWithClient('client.zUnionStore', async client => { - assert.equal( - await client.zUnionStore('destination', 'key'), - 0 - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zUnionStore', async client => { +// assert.equal( +// await client.zUnionStore('destination', 'key'), +// 0 +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZUNIONSTORE.ts b/packages/client/lib/commands/ZUNIONSTORE.ts index 2a42e21bc87..dab0417fac9 100644 --- a/packages/client/lib/commands/ZUNIONSTORE.ts +++ b/packages/client/lib/commands/ZUNIONSTORE.ts @@ -1,29 +1,29 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; -import { pushVerdictArgument } from './generic-transformers'; +// import { RedisCommandArgument, RedisCommandArguments } from '.'; +// import { pushVariadicArgument } from './generic-transformers'; -export const FIRST_KEY_INDEX = 1; +// export const FIRST_KEY_INDEX = 1; -interface ZUnionOptions { - WEIGHTS?: Array; - AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; -} +// interface ZUnionOptions { +// WEIGHTS?: Array; +// AGGREGATE?: 'SUM' | 'MIN' | 'MAX'; +// } -export function transformArguments( - destination: RedisCommandArgument, - keys: Array | RedisCommandArgument, - options?: ZUnionOptions -): RedisCommandArguments { - const args = pushVerdictArgument(['ZUNIONSTORE', destination], keys); +// export function transformArguments( +// destination: RedisCommandArgument, +// keys: Array | RedisCommandArgument, +// options?: ZUnionOptions +// ): RedisCommandArguments { +// const args = pushVariadicArgument(['ZUNIONSTORE', destination], keys); - if (options?.WEIGHTS) { - args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString())); - } +// if (options?.WEIGHTS) { +// args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString())); +// } - if (options?.AGGREGATE) { - args.push('AGGREGATE', options.AGGREGATE); - } +// if (options?.AGGREGATE) { +// args.push('AGGREGATE', options.AGGREGATE); +// } - return args; -} +// return args; +// } -export declare function transformReply(): number; +// export declare function transformReply(): number; diff --git a/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts index 3786a97963d..6076801367c 100644 --- a/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.spec.ts @@ -1,48 +1,48 @@ -import { strict as assert } from 'assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './ZUNION_WITHSCORES'; +// import { strict as assert } from 'assert'; +// import testUtils, { GLOBAL } from '../test-utils'; +// import { transformArguments } from './ZUNION_WITHSCORES'; -describe('ZUNION WITHSCORES', () => { - testUtils.isVersionGreaterThanHook([6, 2]); +// describe('ZUNION WITHSCORES', () => { +// testUtils.isVersionGreaterThanHook([6, 2]); - describe('transformArguments', () => { - it('key (string)', () => { - assert.deepEqual( - transformArguments('key'), - ['ZUNION', '1', 'key', 'WITHSCORES'] - ); - }); +// describe('transformArguments', () => { +// it('key (string)', () => { +// assert.deepEqual( +// transformArguments('key'), +// ['ZUNION', '1', 'key', 'WITHSCORES'] +// ); +// }); - it('keys (array)', () => { - assert.deepEqual( - transformArguments(['1', '2']), - ['ZUNION', '2', '1', '2', 'WITHSCORES'] - ); - }); +// it('keys (array)', () => { +// assert.deepEqual( +// transformArguments(['1', '2']), +// ['ZUNION', '2', '1', '2', 'WITHSCORES'] +// ); +// }); - it('with WEIGHTS', () => { - assert.deepEqual( - transformArguments('key', { - WEIGHTS: [1] - }), - ['ZUNION', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES'] - ); - }); +// it('with WEIGHTS', () => { +// assert.deepEqual( +// transformArguments('key', { +// WEIGHTS: [1] +// }), +// ['ZUNION', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES'] +// ); +// }); - it('with AGGREGATE', () => { - assert.deepEqual( - transformArguments('key', { - AGGREGATE: 'SUM' - }), - ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] - ); - }); - }); +// it('with AGGREGATE', () => { +// assert.deepEqual( +// transformArguments('key', { +// AGGREGATE: 'SUM' +// }), +// ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES'] +// ); +// }); +// }); - testUtils.testWithClient('client.zUnionWithScores', async client => { - assert.deepEqual( - await client.zUnionWithScores('key'), - [] - ); - }, GLOBAL.SERVERS.OPEN); -}); +// testUtils.testWithClient('client.zUnionWithScores', async client => { +// assert.deepEqual( +// await client.zUnionWithScores('key'), +// [] +// ); +// }, GLOBAL.SERVERS.OPEN); +// }); diff --git a/packages/client/lib/commands/ZUNION_WITHSCORES.ts b/packages/client/lib/commands/ZUNION_WITHSCORES.ts index 168cc929ac8..54a43da7ecf 100644 --- a/packages/client/lib/commands/ZUNION_WITHSCORES.ts +++ b/packages/client/lib/commands/ZUNION_WITHSCORES.ts @@ -1,13 +1,13 @@ -import { RedisCommandArguments } from '.'; -import { transformArguments as transformZUnionArguments } from './ZUNION'; +// import { RedisCommandArguments } from '.'; +// import { transformArguments as transformZUnionArguments } from './ZUNION'; -export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION'; +// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION'; -export function transformArguments(...args: Parameters): RedisCommandArguments { - return [ - ...transformZUnionArguments(...args), - 'WITHSCORES' - ]; -} +// export function transformArguments(...args: Parameters): RedisCommandArguments { +// return [ +// ...transformZUnionArguments(...args), +// 'WITHSCORES' +// ]; +// } -export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; +// export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers'; diff --git a/packages/client/lib/commands/generic-transformers.spec.ts b/packages/client/lib/commands/generic-transformers.spec.ts index 301cab0a75c..0c64a2175fd 100644 --- a/packages/client/lib/commands/generic-transformers.spec.ts +++ b/packages/client/lib/commands/generic-transformers.spec.ts @@ -18,10 +18,10 @@ import { transformEXAT, transformPXAT, pushEvalArguments, - pushVerdictArguments, - pushVerdictNumberArguments, - pushVerdictArgument, - pushOptionalVerdictArgument, + pushVariadicArguments, + pushVariadicNumberArguments, + pushVariadicArgument, + pushOptionalVariadicArgument, transformCommandReply, CommandFlags, CommandCategories, @@ -564,72 +564,72 @@ describe('Generic Transformers', () => { }); }); - describe('pushVerdictArguments', () => { + describe('pushVariadicArguments', () => { it('string', () => { assert.deepEqual( - pushVerdictArguments([], 'string'), + pushVariadicArguments([], 'string'), ['string'] ); }); it('array', () => { assert.deepEqual( - pushVerdictArguments([], ['1', '2']), + pushVariadicArguments([], ['1', '2']), ['1', '2'] ); }); }); - describe('pushVerdictNumberArguments', () => { + describe('pushVariadicNumberArguments', () => { it('number', () => { assert.deepEqual( - pushVerdictNumberArguments([], 0), + pushVariadicNumberArguments([], 0), ['0'] ); }); it('array', () => { assert.deepEqual( - pushVerdictNumberArguments([], [0, 1]), + pushVariadicNumberArguments([], [0, 1]), ['0', '1'] ); }); }); - describe('pushVerdictArgument', () => { + describe('pushVariadicArgument', () => { it('string', () => { assert.deepEqual( - pushVerdictArgument([], 'string'), + pushVariadicArgument([], 'string'), ['1', 'string'] ); }); it('array', () => { assert.deepEqual( - pushVerdictArgument([], ['1', '2']), + pushVariadicArgument([], ['1', '2']), ['2', '1', '2'] ); }); }); - describe('pushOptionalVerdictArgument', () => { + describe('pushOptionalVariadicArgument', () => { it('undefined', () => { assert.deepEqual( - pushOptionalVerdictArgument([], 'name', undefined), + pushOptionalVariadicArgument([], 'name', undefined), [] ); }); it('string', () => { assert.deepEqual( - pushOptionalVerdictArgument([], 'name', 'string'), + pushOptionalVariadicArgument([], 'name', 'string'), ['name', '1', 'string'] ); }); it('array', () => { assert.deepEqual( - pushOptionalVerdictArgument([], 'name', ['1', '2']), + pushOptionalVariadicArgument([], 'name', ['1', '2']), ['name', '2', '1', '2'] ); }); diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 5048de9399a..9a8eef476e4 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -1,234 +1,210 @@ -import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ArrayReply, BlobStringReply, CommandArguments, DoubleReply, NullReply, RedisArgument, Resp2Reply } from '../RESP/types'; -export function transformBooleanReply(reply: number): boolean { - return reply === 1; -} +// export function transformBooleanReply(reply: number): boolean { +// return reply === 1; +// } -export function transformBooleanArrayReply(reply: Array): Array { - return reply.map(transformBooleanReply); -} +// export function transformBooleanArrayReply(reply: Array): Array { +// return reply.map(transformBooleanReply); +// } export type BitValue = 0 | 1; -export interface ScanOptions { - MATCH?: string; - COUNT?: number; -} - -export function pushScanArguments( - args: RedisCommandArguments, - cursor: number, - options?: ScanOptions -): RedisCommandArguments { - args.push(cursor.toString()); - - if (options?.MATCH) { - args.push('MATCH', options.MATCH); - } - - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } - - return args; -} - -export function transformNumberInfinityReply(reply: RedisCommandArgument): number { - switch (reply.toString()) { - case '+inf': - return Infinity; +export function transformDoubleReply(reply: BlobStringReply): number { + switch (reply.toString()) { + case '+inf': + return Infinity; - case '-inf': - return -Infinity; + case '-inf': + return -Infinity; - default: - return Number(reply); - } + default: + return Number(reply); + } } -export function transformNumberInfinityNullReply(reply: RedisCommandArgument | null): number | null { - if (reply === null) return null; +export function transformNullableDoubleReply(reply: BlobStringReply | NullReply): number | null { + if (reply === null) return null; - return transformNumberInfinityReply(reply); + return transformDoubleReply(reply); } -export function transformNumberInfinityNullArrayReply(reply: Array): Array { - return reply.map(transformNumberInfinityNullReply); +export function transformArrayNullableDoubleReply(reply: Array): Array { + return reply.map(transformNullableDoubleReply); } -export function transformNumberInfinityArgument(num: number): string { - switch (num) { - case Infinity: - return '+inf'; +export function transformDoubleArgument(num: number): string { + switch (num) { + case Infinity: + return '+inf'; - case -Infinity: - return '-inf'; + case -Infinity: + return '-inf'; - default: - return num.toString(); - } + default: + return num.toString(); + } } -export function transformStringNumberInfinityArgument(num: RedisCommandArgument | number): RedisCommandArgument { - if (typeof num !== 'number') return num; +export function transformStringDoubleArgument(num: RedisArgument | number): RedisArgument { + if (typeof num !== 'number') return num; - return transformNumberInfinityArgument(num); + return transformDoubleArgument(num); } export function transformTuplesReply( - reply: Array -): Record { - const message = Object.create(null); + reply: ArrayReply +): Record { + const message = Object.create(null); - for (let i = 0; i < reply.length; i += 2) { - message[reply[i].toString()] = reply[i + 1]; - } + for (let i = 0; i < reply.length; i += 2) { + message[reply[i].toString()] = reply[i + 1]; + } - return message; + return message; } export interface StreamMessageReply { - id: RedisCommandArgument; - message: Record; + id: RedisArgument; + message: Record; } export type StreamMessagesReply = Array; export function transformStreamMessagesReply(reply: Array): StreamMessagesReply { - const messages = []; + const messages = []; - for (const [id, message] of reply) { - messages.push({ - id, - message: transformTuplesReply(message) - }); - } + for (const [id, message] of reply) { + messages.push({ + id, + message: transformTuplesReply(message) + }); + } - return messages; + return messages; } export type StreamsMessagesReply = Array<{ - name: RedisCommandArgument; - messages: StreamMessagesReply; + name: RedisArgument; + messages: StreamMessagesReply; }> | null; export function transformStreamsMessagesReply(reply: Array | null): StreamsMessagesReply | null { - if (reply === null) return null; + if (reply === null) return null; - return reply.map(([name, rawMessages]) => ({ - name, - messages: transformStreamMessagesReply(rawMessages) - })); + return reply.map(([name, rawMessages]) => ({ + name, + messages: transformStreamMessagesReply(rawMessages) + })); } export interface ZMember { - score: number; - value: RedisCommandArgument; + score: number; + value: RedisArgument; } export function transformSortedSetMemberNullReply( - reply: [RedisCommandArgument, RedisCommandArgument] | [] + reply: [BlobStringReply, BlobStringReply] | [] ): ZMember | null { - if (!reply.length) return null; + if (!reply.length) return null; - return transformSortedSetMemberReply(reply); + return transformSortedSetMemberReply(reply); } export function transformSortedSetMemberReply( - reply: [RedisCommandArgument, RedisCommandArgument] + reply: [BlobStringReply, BlobStringReply] ): ZMember { - return { - value: reply[0], - score: transformNumberInfinityReply(reply[1]) - }; + return { + value: reply[0], + score: transformDoubleReply(reply[1]) + }; } -export function transformSortedSetWithScoresReply(reply: Array): Array { +export const transformSortedSetReply = { + 2: (reply: ArrayReply) => { const members = []; - for (let i = 0; i < reply.length; i += 2) { - members.push({ - value: reply[i], - score: transformNumberInfinityReply(reply[i + 1]) - }); + members.push({ + value: reply[i], + score: transformDoubleReply(reply[i + 1]) + }); } return members; + }, + 3: (reply: ArrayReply<[BlobStringReply, DoubleReply]>) => { + return reply.map(([value, score]) => ({ + value, + score + })); + } } -export type SortedSetSide = 'MIN' | 'MAX'; - -export interface ZMPopOptions { - COUNT?: number; -} - -export function transformZMPopArguments( - args: RedisCommandArguments, - keys: RedisCommandArgument | Array, - side: SortedSetSide, - options?: ZMPopOptions -): RedisCommandArguments { - pushVerdictArgument(args, keys); +export function transformSortedSetWithScoresReply(reply: ArrayReply): Array { + const members = []; - args.push(side); - - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } + for (let i = 0; i < reply.length; i += 2) { + members.push({ + value: reply[i], + score: transformDoubleReply(reply[i + 1]) + }); + } - return args; + return members; } export type ListSide = 'LEFT' | 'RIGHT'; +export type SortedSetSide = 'MIN' | 'MAX'; + export interface LMPopOptions { - COUNT?: number; + COUNT?: number; } export function transformLMPopArguments( - args: RedisCommandArguments, - keys: RedisCommandArgument | Array, - side: ListSide, - options?: LMPopOptions -): RedisCommandArguments { - pushVerdictArgument(args, keys); + args: CommandArguments, + keys: RedisVariadicArgument, + side: ListSide, + options?: LMPopOptions +): CommandArguments { + pushVariadicArgument(args, keys); - args.push(side); + args.push(side); - if (options?.COUNT) { - args.push('COUNT', options.COUNT.toString()); - } + if (options?.COUNT) { + args.push('COUNT', options.COUNT.toString()); + } - return args; + return args; } type GeoCountArgument = number | { - value: number; - ANY?: true + value: number; + ANY?: true }; export function pushGeoCountArgument( - args: RedisCommandArguments, - count: GeoCountArgument | undefined -): RedisCommandArguments { - if (typeof count === 'number') { - args.push('COUNT', count.toString()); - } else if (count) { - args.push('COUNT', count.value.toString()); - - if (count.ANY) { - args.push('ANY'); - } + args: CommandArguments, + count: GeoCountArgument | undefined +): CommandArguments { + if (typeof count === 'number') { + args.push('COUNT', count.toString()); + } else if (count) { + args.push('COUNT', count.value.toString()); + + if (count.ANY) { + args.push('ANY'); } + } - return args; + return args; } export type GeoUnits = 'm' | 'km' | 'mi' | 'ft'; export interface GeoCoordinates { - longitude: string | number; - latitude: string | number; + longitude: RedisArgument | number; + latitude: RedisArgument | number; } type GeoSearchFromMember = string; @@ -236,454 +212,412 @@ type GeoSearchFromMember = string; export type GeoSearchFrom = GeoSearchFromMember | GeoCoordinates; interface GeoSearchByRadius { - radius: number; - unit: GeoUnits; + radius: number; + unit: GeoUnits; } interface GeoSearchByBox { - width: number; - height: number; - unit: GeoUnits; + width: number; + height: number; + unit: GeoUnits; } export type GeoSearchBy = GeoSearchByRadius | GeoSearchByBox; export interface GeoSearchOptions { - SORT?: 'ASC' | 'DESC'; - COUNT?: GeoCountArgument; + SORT?: 'ASC' | 'DESC'; + COUNT?: GeoCountArgument; } export function pushGeoSearchArguments( - args: RedisCommandArguments, - key: RedisCommandArgument, - from: GeoSearchFrom, - by: GeoSearchBy, - options?: GeoSearchOptions -): RedisCommandArguments { - args.push(key); - - if (typeof from === 'string') { - args.push('FROMMEMBER', from); - } else { - args.push('FROMLONLAT', from.longitude.toString(), from.latitude.toString()); - } + args: CommandArguments, + key: RedisArgument, + from: GeoSearchFrom, + by: GeoSearchBy, + options?: GeoSearchOptions +): CommandArguments { + args.push(key); - if ('radius' in by) { - args.push('BYRADIUS', by.radius.toString()); - } else { - args.push('BYBOX', by.width.toString(), by.height.toString()); - } + if (typeof from === 'string') { + args.push('FROMMEMBER', from); + } else { + args.push('FROMLONLAT', from.longitude.toString(), from.latitude.toString()); + } - args.push(by.unit); + if ('radius' in by) { + args.push('BYRADIUS', by.radius.toString()); + } else { + args.push('BYBOX', by.width.toString(), by.height.toString()); + } - if (options?.SORT) { - args.push(options.SORT); - } + args.push(by.unit); + + if (options?.SORT) { + args.push(options.SORT); + } - pushGeoCountArgument(args, options?.COUNT); + pushGeoCountArgument(args, options?.COUNT); - return args; + return args; } export function pushGeoRadiusArguments( - args: RedisCommandArguments, - key: RedisCommandArgument, - from: GeoSearchFrom, - radius: number, - unit: GeoUnits, - options?: GeoSearchOptions -): RedisCommandArguments { - args.push(key); - - if (typeof from === 'string') { - args.push(from); - } else { - args.push( - from.longitude.toString(), - from.latitude.toString() - ); - } - + args: CommandArguments, + key: RedisArgument, + from: GeoSearchFrom, + radius: number, + unit: GeoUnits, + options?: GeoSearchOptions +): CommandArguments { + args.push(key); + + if (typeof from === 'string') { + args.push(from); + } else { args.push( - radius.toString(), - unit + from.longitude.toString(), + from.latitude.toString() ); + } - if (options?.SORT) { - args.push(options.SORT); - } + args.push( + radius.toString(), + unit + ); - pushGeoCountArgument(args, options?.COUNT); + if (options?.SORT) { + args.push(options.SORT); + } - return args; + pushGeoCountArgument(args, options?.COUNT); + + return args; } export interface GeoRadiusStoreOptions extends GeoSearchOptions { - STOREDIST?: boolean; + STOREDIST?: boolean; } export function pushGeoRadiusStoreArguments( - args: RedisCommandArguments, - key: RedisCommandArgument, - from: GeoSearchFrom, - radius: number, - unit: GeoUnits, - destination: RedisCommandArgument, - options?: GeoRadiusStoreOptions -): RedisCommandArguments { - pushGeoRadiusArguments(args, key, from, radius, unit, options); - - if (options?.STOREDIST) { - args.push('STOREDIST', destination); - } else { - args.push('STORE', destination); - } + args: CommandArguments, + key: RedisArgument, + from: GeoSearchFrom, + radius: number, + unit: GeoUnits, + destination: RedisArgument, + options?: GeoRadiusStoreOptions +): CommandArguments { + pushGeoRadiusArguments(args, key, from, radius, unit, options); - return args; + if (options?.STOREDIST) { + args.push('STOREDIST', destination); + } else { + args.push('STORE', destination); + } + + return args; } export enum GeoReplyWith { - DISTANCE = 'WITHDIST', - HASH = 'WITHHASH', - COORDINATES = 'WITHCOORD' + DISTANCE = 'WITHDIST', + HASH = 'WITHHASH', + COORDINATES = 'WITHCOORD' } export interface GeoReplyWithMember { - member: string; - distance?: number; - hash?: string; - coordinates?: { - longitude: string; - latitude: string; - }; + member: string; + distance?: number; + hash?: string; + coordinates?: { + longitude: string; + latitude: string; + }; } export function transformGeoMembersWithReply(reply: Array>, replyWith: Array): Array { - const replyWithSet = new Set(replyWith); - - let index = 0; - const distanceIndex = replyWithSet.has(GeoReplyWith.DISTANCE) && ++index, - hashIndex = replyWithSet.has(GeoReplyWith.HASH) && ++index, - coordinatesIndex = replyWithSet.has(GeoReplyWith.COORDINATES) && ++index; - - return reply.map(member => { - const transformedMember: GeoReplyWithMember = { - member: member[0] - }; - - if (distanceIndex) { - transformedMember.distance = member[distanceIndex]; - } - - if (hashIndex) { - transformedMember.hash = member[hashIndex]; - } - - if (coordinatesIndex) { - const [longitude, latitude] = member[coordinatesIndex]; - transformedMember.coordinates = { - longitude, - latitude - }; - } - - return transformedMember; - }); + const replyWithSet = new Set(replyWith); + + let index = 0; + const distanceIndex = replyWithSet.has(GeoReplyWith.DISTANCE) && ++index, + hashIndex = replyWithSet.has(GeoReplyWith.HASH) && ++index, + coordinatesIndex = replyWithSet.has(GeoReplyWith.COORDINATES) && ++index; + + return reply.map(member => { + const transformedMember: GeoReplyWithMember = { + member: member[0] + }; + + if (distanceIndex) { + transformedMember.distance = member[distanceIndex]; + } + + if (hashIndex) { + transformedMember.hash = member[hashIndex]; + } + + if (coordinatesIndex) { + const [longitude, latitude] = member[coordinatesIndex]; + transformedMember.coordinates = { + longitude, + latitude + }; + } + + return transformedMember; + }); } export function transformEXAT(EXAT: number | Date): string { - return (typeof EXAT === 'number' ? EXAT : Math.floor(EXAT.getTime() / 1000)).toString(); + return (typeof EXAT === 'number' ? EXAT : Math.floor(EXAT.getTime() / 1000)).toString(); } export function transformPXAT(PXAT: number | Date): string { - return (typeof PXAT === 'number' ? PXAT : PXAT.getTime()).toString(); + return (typeof PXAT === 'number' ? PXAT : PXAT.getTime()).toString(); } export interface EvalOptions { - keys?: Array; - arguments?: Array; + keys?: Array; + arguments?: Array; } export function evalFirstKeyIndex(options?: EvalOptions): string | undefined { - return options?.keys?.[0]; + return options?.keys?.[0]; } export function pushEvalArguments(args: Array, options?: EvalOptions): Array { - if (options?.keys) { - args.push( - options.keys.length.toString(), - ...options.keys - ); - } else { - args.push('0'); - } + if (options?.keys) { + args.push( + options.keys.length.toString(), + ...options.keys + ); + } else { + args.push('0'); + } - if (options?.arguments) { - args.push(...options.arguments); - } + if (options?.arguments) { + args.push(...options.arguments); + } - return args; + return args; } -export function pushVerdictArguments(args: RedisCommandArguments, value: RedisCommandArgument | Array): RedisCommandArguments { - if (Array.isArray(value)) { - // https://github.com/redis/node-redis/pull/2160 - args = args.concat(value); - } else { - args.push(value); - } +export function pushVariadicArguments(args: CommandArguments, value: RedisVariadicArgument): CommandArguments { + if (Array.isArray(value)) { + // https://github.com/redis/node-redis/pull/2160 + args = args.concat(value); + } else { + args.push(value); + } - return args; + return args; } -export function pushVerdictNumberArguments( - args: RedisCommandArguments, - value: number | Array -): RedisCommandArguments { - if (Array.isArray(value)) { - for (const item of value) { - args.push(item.toString()); - } - } else { - args.push(value.toString()); +export function pushVariadicNumberArguments( + args: CommandArguments, + value: number | Array +): CommandArguments { + if (Array.isArray(value)) { + for (const item of value) { + args.push(item.toString()); } + } else { + args.push(value.toString()); + } - return args; + return args; } -export function pushVerdictArgument( - args: RedisCommandArguments, - value: RedisCommandArgument | Array -): RedisCommandArguments { - if (Array.isArray(value)) { - args.push(value.length.toString(), ...value); - } else { - args.push('1', value); - } +export type RedisVariadicArgument = RedisArgument | Array; + +export function pushVariadicArgument( + args: Array, + value: RedisVariadicArgument +): CommandArguments { + if (Array.isArray(value)) { + args.push(value.length.toString(), ...value); + } else { + args.push('1', value); + } - return args; + return args; } -export function pushOptionalVerdictArgument( - args: RedisCommandArguments, - name: RedisCommandArgument, - value: undefined | RedisCommandArgument | Array -): RedisCommandArguments { - if (value === undefined) return args; +export function pushOptionalVariadicArgument( + args: CommandArguments, + name: RedisArgument, + value?: RedisVariadicArgument +): CommandArguments { + if (value === undefined) return args; - args.push(name); + args.push(name); - return pushVerdictArgument(args, value); + return pushVariadicArgument(args, value); } export enum CommandFlags { - WRITE = 'write', // command may result in modifications - READONLY = 'readonly', // command will never modify keys - DENYOOM = 'denyoom', // reject command if currently out of memory - ADMIN = 'admin', // server admin command - PUBSUB = 'pubsub', // pubsub-related command - NOSCRIPT = 'noscript', // deny this command from scripts - RANDOM = 'random', // command has random results, dangerous for scripts - SORT_FOR_SCRIPT = 'sort_for_script', // if called from script, sort output - LOADING = 'loading', // allow command while database is loading - STALE = 'stale', // allow command while replica has stale data - SKIP_MONITOR = 'skip_monitor', // do not show this command in MONITOR - ASKING = 'asking', // cluster related - accept even if importing - FAST = 'fast', // command operates in constant or log(N) time. Used for latency monitoring. - MOVABLEKEYS = 'movablekeys' // keys have no pre-determined position. You must discover keys yourself. + WRITE = 'write', // command may result in modifications + READONLY = 'readonly', // command will never modify keys + DENYOOM = 'denyoom', // reject command if currently out of memory + ADMIN = 'admin', // server admin command + PUBSUB = 'pubsub', // pubsub-related command + NOSCRIPT = 'noscript', // deny this command from scripts + RANDOM = 'random', // command has random results, dangerous for scripts + SORT_FOR_SCRIPT = 'sort_for_script', // if called from script, sort output + LOADING = 'loading', // allow command while database is loading + STALE = 'stale', // allow command while replica has stale data + SKIP_MONITOR = 'skip_monitor', // do not show this command in MONITOR + ASKING = 'asking', // cluster related - accept even if importing + FAST = 'fast', // command operates in constant or log(N) time. Used for latency monitoring. + MOVABLEKEYS = 'movablekeys' // keys have no pre-determined position. You must discover keys yourself. } export enum CommandCategories { - KEYSPACE = '@keyspace', - READ = '@read', - WRITE = '@write', - SET = '@set', - SORTEDSET = '@sortedset', - LIST = '@list', - HASH = '@hash', - STRING = '@string', - BITMAP = '@bitmap', - HYPERLOGLOG = '@hyperloglog', - GEO = '@geo', - STREAM = '@stream', - PUBSUB = '@pubsub', - ADMIN = '@admin', - FAST = '@fast', - SLOW = '@slow', - BLOCKING = '@blocking', - DANGEROUS = '@dangerous', - CONNECTION = '@connection', - TRANSACTION = '@transaction', - SCRIPTING = '@scripting' + KEYSPACE = '@keyspace', + READ = '@read', + WRITE = '@write', + SET = '@set', + SORTEDSET = '@sortedset', + LIST = '@list', + HASH = '@hash', + STRING = '@string', + BITMAP = '@bitmap', + HYPERLOGLOG = '@hyperloglog', + GEO = '@geo', + STREAM = '@stream', + PUBSUB = '@pubsub', + ADMIN = '@admin', + FAST = '@fast', + SLOW = '@slow', + BLOCKING = '@blocking', + DANGEROUS = '@dangerous', + CONNECTION = '@connection', + TRANSACTION = '@transaction', + SCRIPTING = '@scripting' } export type CommandRawReply = [ - name: string, - arity: number, - flags: Array, - firstKeyIndex: number, - lastKeyIndex: number, - step: number, - categories: Array + name: string, + arity: number, + flags: Array, + firstKeyIndex: number, + lastKeyIndex: number, + step: number, + categories: Array ]; export type CommandReply = { - name: string, - arity: number, - flags: Set, - firstKeyIndex: number, - lastKeyIndex: number, - step: number, - categories: Set + name: string, + arity: number, + flags: Set, + firstKeyIndex: number, + lastKeyIndex: number, + step: number, + categories: Set }; export function transformCommandReply( - this: void, - [name, arity, flags, firstKeyIndex, lastKeyIndex, step, categories]: CommandRawReply + this: void, + [name, arity, flags, firstKeyIndex, lastKeyIndex, step, categories]: CommandRawReply ): CommandReply { - return { - name, - arity, - flags: new Set(flags), - firstKeyIndex, - lastKeyIndex, - step, - categories: new Set(categories) - }; + return { + name, + arity, + flags: new Set(flags), + firstKeyIndex, + lastKeyIndex, + step, + categories: new Set(categories) + }; } export enum RedisFunctionFlags { - NO_WRITES = 'no-writes', - ALLOW_OOM = 'allow-oom', - ALLOW_STALE = 'allow-stale', - NO_CLUSTER = 'no-cluster' + NO_WRITES = 'no-writes', + ALLOW_OOM = 'allow-oom', + ALLOW_STALE = 'allow-stale', + NO_CLUSTER = 'no-cluster' } export type FunctionListRawItemReply = [ - 'library_name', - string, - 'engine', + 'library_name', + string, + 'engine', + string, + 'functions', + Array<[ + 'name', string, - 'functions', - Array<[ - 'name', - string, - 'description', - string | null, - 'flags', - Array - ]> + 'description', + string | null, + 'flags', + Array + ]> ]; export interface FunctionListItemReply { - libraryName: string; - engine: string; - functions: Array<{ - name: string; - description: string | null; - flags: Array; - }>; + libraryName: string; + engine: string; + functions: Array<{ + name: string; + description: string | null; + flags: Array; + }>; } export function transformFunctionListItemReply(reply: FunctionListRawItemReply): FunctionListItemReply { - return { - libraryName: reply[1], - engine: reply[3], - functions: reply[5].map(fn => ({ - name: fn[1], - description: fn[3], - flags: fn[5] - })) - }; -} - -export interface SortOptions { - BY?: string; - LIMIT?: { - offset: number; - count: number; - }, - GET?: string | Array; - DIRECTION?: 'ASC' | 'DESC'; - ALPHA?: true; -} - -export function pushSortArguments( - args: RedisCommandArguments, - options?: SortOptions -): RedisCommandArguments { - if (options?.BY) { - args.push('BY', options.BY); - } - - if (options?.LIMIT) { - args.push( - 'LIMIT', - options.LIMIT.offset.toString(), - options.LIMIT.count.toString() - ); - } - - if (options?.GET) { - for (const pattern of (typeof options.GET === 'string' ? [options.GET] : options.GET)) { - args.push('GET', pattern); - } - } - - if (options?.DIRECTION) { - args.push(options.DIRECTION); - } - - if (options?.ALPHA) { - args.push('ALPHA'); - } - - return args; + return { + libraryName: reply[1], + engine: reply[3], + functions: reply[5].map(fn => ({ + name: fn[1], + description: fn[3], + flags: fn[5] + })) + }; } export interface SlotRange { - start: number; - end: number; + start: number; + end: number; } function pushSlotRangeArguments( - args: RedisCommandArguments, - range: SlotRange + args: CommandArguments, + range: SlotRange ): void { - args.push( - range.start.toString(), - range.end.toString() - ); + args.push( + range.start.toString(), + range.end.toString() + ); } export function pushSlotRangesArguments( - args: RedisCommandArguments, - ranges: SlotRange | Array -): RedisCommandArguments { - if (Array.isArray(ranges)) { - for (const range of ranges) { - pushSlotRangeArguments(args, range); - } - } else { - pushSlotRangeArguments(args, ranges); + args: CommandArguments, + ranges: SlotRange | Array +): CommandArguments { + if (Array.isArray(ranges)) { + for (const range of ranges) { + pushSlotRangeArguments(args, range); } + } else { + pushSlotRangeArguments(args, ranges); + } - return args; + return args; } export type RawRangeReply = [ - start: number, - end: number + start: number, + end: number ]; export interface RangeReply { - start: number; - end: number; + start: number; + end: number; } export function transformRangeReply([start, end]: RawRangeReply): RangeReply { - return { - start, - end - }; + return { + start, + end + }; } diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 60f9720c8d1..04e1f44b096 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -1,91 +1,485 @@ -import { ClientCommandOptions } from '../client'; -import { CommandOptions } from '../command-options'; -import { RedisScriptConfig, SHA1 } from '../lua-script'; +import ACL_CAT from './ACL_CAT'; +import ACL_DRYRUN from './ACL_DRYRUN'; +import ACL_GENPASS from './ACL_GENPASS'; +import ACL_GETUSER from './ACL_GETUSER'; +import ACL_LIST from './ACL_LIST'; +import ACL_LOAD from './ACL_LOAD'; +import ACL_LOG_RESET from './ACL_LOG_RESET'; +import ACL_LOG from './ACL_LOG'; +import ACL_SAVE from './ACL_SAVE'; +import ACL_SETUSER from './ACL_SETUSER'; +import ACL_USERS from './ACL_USERS'; +import ACL_WHOAMI from './ACL_WHOAMI'; +import APPEND from './APPEND'; +import ASKING from './ASKING'; +import AUTH from './AUTH'; +import BGREWRITEAOF from './BGREWRITEAOF'; +import BGSAVE from './BGSAVE'; +import BITCOUNT from './BITCOUNT'; +import BITFIELD_RO from './BITFIELD_RO'; +import BITFIELD from './BITFIELD'; +import BITOP from './BITOP'; +import BITPOS from './BITPOS'; +import BLMOVE from './BLMOVE'; +import BLMPOP from './BLMPOP'; +import BLPOP from './BLPOP'; +import BRPOP from './BRPOP'; +import BRPOPLPUSH from './BRPOPLPUSH'; +import CLIENT_CACHING from './CLIENT_CACHING'; +import CLIENT_GETNAME from './CLIENT_GETNAME'; +import CLIENT_GETREDIR from './CLIENT_GETREDIR'; +import CLIENT_ID from './CLIENT_ID'; +import CLIENT_INFO from './CLIENT_INFO'; +import CLIENT_KILL from './CLIENT_KILL'; +import CLIENT_LIST from './CLIENT_LIST'; +import CLIENT_NO_EVICT from './CLIENT_NO-EVICT'; +import CLIENT_PAUSE from './CLIENT_PAUSE'; +import CLIENT_SETNAME from './CLIENT_SETNAME'; +import CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS'; +import CLUSTER_SLOTS from './CLUSTER_SLOTS'; +import CLUSTER_MEET from './CLUSTER_MEET'; +import CLUSTER_MYID from './CLUSTER_MYID'; +import CLUSTER_REPLICATE from './CLUSTER_REPLICATE'; +import DECR from './DECR'; +import DECRBY from './DECRBY'; +import GET from './GET'; +import GETDEL from './GETDEL'; +import GETEX from './GETEX'; +import GETRANGE from './GETRANGE'; +import GETSET from './GETSET'; +import FLUSHALL from './FLUSHALL'; +import HDEL from './HDEL'; +import HEXISTS from './HEXISTS'; +import HGET from './HGET'; +import HGETALL from './HGETALL'; +import HINCRBY from './HINCRBY'; +import HINCRBYFLOAT from './HINCRBYFLOAT'; +import HKEYS from './HKEYS'; +import HLEN from './HLEN' +import HMGET from './HMGET'; +import HRANDFIELD_COUNT_WITHVALUES from './HRANDFIELD_COUNT_WITHVALUES'; +import HRANDFIELD_COUNT from './HRANDFIELD_COUNT'; +import HRANDFIELD from './HRANDFIELD'; +import HSCAN from './HSCAN'; +import HSET from './HSET'; +import HSETNX from './HSETNX'; +import HSTRLEN from './HSTRLEN'; +import HVALS from './HVALS'; +import INCR from './INCR'; +import INCRBY from './INCRBY'; +import INCRBYFLOAT from './INCRBYFLOAT'; +import INFO from './INFO'; +// import LCS_IDX_WITHMATCHLEN from './LCS_IDX_WITHMATCHLEN'; +// import LCS_IDX from './LCS_IDX'; +import LCS_LEN from './LCS_LEN'; +import LCS from './LCS'; +import LINDEX from './LINDEX'; +import LINSERT from './LINSERT'; +import LLEN from './LLEN'; +import LMOVE from './LMOVE'; +import LMPOP from './LMPOP'; +import LPOP_COUNT from './LPOP_COUNT'; +import LPOP from './LPOP'; +import LPOS_COUNT from './LPOS_COUNT'; +import LPOS from './LPOS'; +import LPUSH from './LPUSH'; +import LPUSHX from './LPUSHX'; +import LRANGE from './LRANGE'; +import LREM from './LREM'; +import LSET from './LSET'; +import LTRIM from './LTRIM'; +import MGET from './MGET'; +import MSET from './MSET'; +import MSETNX from './MSETNX'; +import PFADD from './PFADD'; +import PFCOUNT from './PFCOUNT'; +import PFMERGE from './PFMERGE'; +import PING from './PING'; +import PSETEX from './PSETEX'; +import RENAME from './RENAME'; +import RENAMENX from './RENAMENX'; +import RPOP_COUNT from './RPOP_COUNT'; +import RPOP from './RPOP'; +import RPOPLPUSH from './RPOPLPUSH'; +import RPUSH from './RPUSH'; +import RPUSHX from './RPUSHX'; +import SADD from './SADD'; +import SCAN from './SCAN'; +import SCARD from './SCARD'; +import SDIFF from './SDIFF'; +import SDIFFSTORE from './SDIFFSTORE'; +import SET from './SET'; +import SETBIT from './SETBIT'; +import SETEX from './SETEX'; +import SETNX from './SETNX'; +import SETRANGE from './SETRANGE'; +import SINTER from './SINTER'; +import SINTERCARD from './SINTERCARD'; +import SINTERSTORE from './SINTERSTORE'; +import SISMEMBER from './SISMEMBER'; +import SMEMBERS from './SMEMBERS'; +import SMISMEMBER from './SMISMEMBER'; +import SORT_RO from './SORT_RO'; +import SORT_STORE from './SORT_STORE'; +import SORT from './SORT'; +import SPUBLISH from './SPUBLISH'; +import SRANDMEMBER_COUNT from './SRANDMEMBER_COUNT'; +import SRANDMEMBER from './SRANDMEMBER'; +import SREM from './SREM'; +import SSCAN from './SSCAN'; +import STRLEN from './STRLEN'; +import TOUCH from './TOUCH'; +import TTL from './TTL'; +import TYPE from './TYPE'; +import UNLINK from './UNLINK'; +import UNWATCH from './UNWATCH'; +import WAIT from './WAIT'; +import WATCH from './WATCH'; +import XLEN from './XLEN'; +import ZADD from './ZADD'; +import ZCARD from './ZCARD'; +import ZCOUNT from './ZCOUNT'; +import ZDIFF_WITHSCORES from './ZDIFF_WITHSCORES'; +import ZDIFF from './ZDIFF'; +import ZDIFFSTORE from './ZDIFFSTORE'; +import ZINCRBY from './ZINCRBY'; +import ZINTER_WITHSCORES from './ZINTER_WITHSCORES'; +import ZINTER from './ZINTER'; +import ZINTERCARD from './ZINTERCARD'; +import ZINTERSTORE from './ZINTERSTORE'; +import ZLEXCOUNT from './ZLEXCOUNT'; +import ZMSCORE from './ZMSCORE'; +import ZRANDMEMBER_COUNT_WITHSCORES from './ZRANDMEMBER_COUNT_WITHSCORES'; +import ZRANDMEMBER_COUNT from './ZRANDMEMBER_COUNT'; +import ZRANDMEMBER from './ZRANDMEMBER'; +import ZRANGE from './ZRANGE'; +import ZRANK from './ZRANK'; +import ZREM from './ZREM'; +import ZREVRANK from './ZREVRANK'; +import ZSCAN from './ZSCAN'; +import ZSCORE from './ZSCORE'; +import { Command } from '../RESP/types'; -export type RedisCommandRawReply = string | number | Buffer | null | undefined | Array; - -export type RedisCommandArgument = string | Buffer; - -export type RedisCommandArguments = Array & { preserve?: unknown }; - -export interface RedisCommand { - FIRST_KEY_INDEX?: number | ((...args: Array) => RedisCommandArgument | undefined); - IS_READ_ONLY?: boolean; - TRANSFORM_LEGACY_REPLY?: boolean; - transformArguments(this: void, ...args: Array): RedisCommandArguments; - transformReply?(this: void, reply: any, preserved?: any): any; -} - -export type RedisCommandReply = - C['transformReply'] extends (...args: any) => infer T ? T : RedisCommandRawReply; - -export type ConvertArgumentType = - Type extends RedisCommandArgument ? ( - Type extends (string & ToType) ? Type : ToType - ) : ( - Type extends Set ? Set> : ( - Type extends Map ? Map> : ( - Type extends Array ? Array> : ( - Type extends Date ? Type : ( - Type extends Record ? { - [Property in keyof Type]: ConvertArgumentType - } : Type - ) - ) - ) - ) - ); - -export type RedisCommandSignature< - Command extends RedisCommand, - Params extends Array = Parameters -> = >( - ...args: Params | [options: Options, ...rest: Params] -) => Promise< - ConvertArgumentType< - RedisCommandReply, - Options['returnBuffers'] extends true ? Buffer : string - > ->; - -export interface RedisCommands { - [command: string]: RedisCommand; -} - -export interface RedisModule { - [command: string]: RedisCommand; -} - -export interface RedisModules { - [module: string]: RedisModule; -} - -export interface RedisFunction extends RedisCommand { - NUMBER_OF_KEYS?: number; -} - -export interface RedisFunctionLibrary { - [fn: string]: RedisFunction; -} - -export interface RedisFunctions { - [library: string]: RedisFunctionLibrary; -} - -export type RedisScript = RedisScriptConfig & SHA1; - -export interface RedisScripts { - [script: string]: RedisScript; -} - -export interface RedisExtensions< - M extends RedisModules = RedisModules, - F extends RedisFunctions = RedisFunctions, - S extends RedisScripts = RedisScripts -> { - modules?: M; - functions?: F; - scripts?: S; -} - -export type ExcludeMappedString = string extends S ? never : S; +export default { + ACL_CAT, + aclCat: ACL_CAT, + ACL_DRYRUN, + aclDryRun: ACL_DRYRUN, + ACL_GENPASS, + aclGenPass: ACL_GENPASS, + ACL_GETUSER, + aclGetUser: ACL_GETUSER, + ACL_LIST, + aclList: ACL_LIST, + ACL_LOAD, + aclLoad: ACL_LOAD, + ACL_LOG_RESET, + aclLogReset: ACL_LOG_RESET, + ACL_LOG, + aclLog: ACL_LOG, + ACL_SAVE, + aclSave: ACL_SAVE, + ACL_SETUSER, + aclSetUser: ACL_SETUSER, + ACL_USERS, + aclUsers: ACL_USERS, + ACL_WHOAMI, + aclWhoAmI: ACL_WHOAMI, + APPEND, + append: APPEND, + ASKING, + asking: ASKING, + AUTH, + auth: AUTH, + BGREWRITEAOF, + bgRewriteAof: BGREWRITEAOF, + BGSAVE, + bgSave: BGSAVE, + BITCOUNT, + bitCount: BITCOUNT, + BITFIELD_RO, + bitFieldRo: BITFIELD_RO, + BITFIELD, + bitField: BITFIELD, + BITOP, + bitOp: BITOP, + BITPOS, + bitPos: BITPOS, + BLMOVE, + blMove: BLMOVE, + BLMPOP, + blmPop: BLMPOP, + BLPOP, + blPop: BLPOP, + BRPOP, + brPop: BRPOP, + BRPOPLPUSH, + brPopLPush: BRPOPLPUSH, + CLIENT_CACHING, + clientCaching: CLIENT_CACHING, + CLIENT_GETNAME, + clientGetName: CLIENT_GETNAME, + CLIENT_GETREDIR, + clientGetRedir: CLIENT_GETREDIR, + CLIENT_ID, + clientId: CLIENT_ID, + CLIENT_INFO, + clientInfo: CLIENT_INFO, + CLIENT_KILL, + clientKill: CLIENT_KILL, + CLIENT_LIST, + clientList: CLIENT_LIST, + 'CLIENT_NO-EVICT': CLIENT_NO_EVICT, + clientNoEvict: CLIENT_NO_EVICT, + CLIENT_PAUSE, + clientPause: CLIENT_PAUSE, + CLIENT_SETNAME, + clientSetName: CLIENT_SETNAME, + CLUSTER_ADDSLOTS, + clusterAddSlots: CLUSTER_ADDSLOTS, + CLUSTER_SLOTS, + clusterSlots: CLUSTER_SLOTS, + CLUSTER_MEET, + clusterMeet: CLUSTER_MEET, + CLUSTER_MYID, + clusterMyId: CLUSTER_MYID, + CLUSTER_REPLICATE, + clusterReplicate: CLUSTER_REPLICATE, + DECR, + decr: DECR, + DECRBY, + decrBy: DECRBY, + GET, + get: GET, + GETDEL, + getDel: GETDEL, + GETEX, + getEx: GETEX, + GETRANGE, + getRange: GETRANGE, + GETSET, + getSet: GETSET, + FLUSHALL, + flushAll: FLUSHALL, + HDEL, + hDel: HDEL, + HEXISTS, + hExists: HEXISTS, + HGET, + hGet: HGET, + HGETALL, + hGetAll: HGETALL, + HINCRBY, + hIncrBy: HINCRBY, + HINCRBYFLOAT, + hIncrByFloat: HINCRBYFLOAT, + HKEYS, + hKeys: HKEYS, + HLEN, + hLen: HLEN, + HMGET, + hMGet: HMGET, + HRANDFIELD_COUNT_WITHVALUES, + hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES, + HRANDFIELD_COUNT, + hRandFieldCount: HRANDFIELD_COUNT, + HRANDFIELD, + hRandField: HRANDFIELD, + HSCAN, + hScan: HSCAN, + HSET, + hSet: HSET, + HSETNX, + hSetNx: HSETNX, + HSTRLEN, + hStrLen: HSTRLEN, + HVALS, + hVals: HVALS, + INCR, + incr: INCR, + INCRBY, + incrBy: INCRBY, + INCRBYFLOAT, + incrByFloat: INCRBYFLOAT, + INFO, + info: INFO, + // LCS_IDX_WITHMATCHLEN, + // LCS_IDX, + LCS_LEN, + lcsLen: LCS_LEN, + LCS, + lcs: LCS, + LINDEX, + lIndex: LINDEX, + LINSERT, + lInsert: LINSERT, + LLEN, + lLen: LLEN, + LMOVE, + lMove: LMOVE, + LMPOP, + lmPop: LMPOP, + LPOP_COUNT, + lPopCount: LPOP_COUNT, + LPOP, + lPop: LPOP, + LPOS_COUNT, + lPosCount: LPOS_COUNT, + LPOS, + lPos: LPOS, + LPUSH, + lPush: LPUSH, + LPUSHX, + lPushX: LPUSHX, + LRANGE, + lRange: LRANGE, + LREM, + lRem: LREM, + LSET, + lSet: LSET, + LTRIM, + lTrim: LTRIM, + MGET, + mGet: MGET, + MSET, + mSet: MSET, + MSETNX, + mSetNx: MSETNX, + PFADD, + pfAdd: PFADD, + PFCOUNT, + pfCount: PFCOUNT, + PFMERGE, + pfMerge: PFMERGE, + PING, + /** + * ping jsdoc + */ + ping: PING, + PSETEX, + pSetEx: PSETEX, + RENAME, + rename: RENAME, + RENAMENX, + renameNX: RENAMENX, + RPOP_COUNT, + rPopCount: RPOP_COUNT, + RPOP, + rPop: RPOP, + RPOPLPUSH, + rPopLPush: RPOPLPUSH, + RPUSH, + rPush: RPUSH, + RPUSHX, + rPushX: RPUSHX, + SADD, + sAdd: SADD, + SCAN, + scan: SCAN, + SCARD, + sCard: SCARD, + SDIFF, + sDiff: SDIFF, + SDIFFSTORE, + sDiffStore: SDIFFSTORE, + SET, + set: SET, + SETBIT, + setBit: SETBIT, + SETEX, + setEx: SETEX, + SETNX, + setNX: SETNX, + SETRANGE, + setRange: SETRANGE, + SINTER, + sInter: SINTER, + SINTERCARD, + sInterCard: SINTERCARD, + SINTERSTORE, + sInterStore: SINTERSTORE, + SISMEMBER, + sIsMember: SISMEMBER, + SMEMBERS, + sMembers: SMEMBERS, + SMISMEMBER, + smIsMember: SMISMEMBER, + SORT_RO, + sortRo: SORT_RO, + SORT_STORE, + sortStore: SORT_STORE, + SORT, + sort: SORT, + SPUBLISH, + sPublish: SPUBLISH, + SRANDMEMBER_COUNT, + sRandMemberCount: SRANDMEMBER_COUNT, + SRANDMEMBER, + sRandMember: SRANDMEMBER, + SREM, + sRem: SREM, + SSCAN, + sScan: SSCAN, + STRLEN, + strLen: STRLEN, + TOUCH, + touch: TOUCH, + TTL, + ttl: TTL, + TYPE, + type: TYPE, + UNLINK, + unlink: UNLINK, + UNWATCH, + unwatch: UNWATCH, + WAIT, + wait: WAIT, + WATCH, + watch: WATCH, + XLEN, + xLen: XLEN, + ZADD, + zAdd: ZADD, + ZCARD, + zCard: ZCARD, + ZCOUNT, + zCount: ZCOUNT, + ZDIFF_WITHSCORES, + zDiffWithScores: ZDIFF_WITHSCORES, + ZDIFF, + zDiff: ZDIFF, + ZDIFFSTORE, + zDiffStore: ZDIFFSTORE, + ZINCRBY, + zIncrBy: ZINCRBY, + ZINTER_WITHSCORES, + zInterWithScores: ZINTER_WITHSCORES, + ZINTER, + zInter: ZINTER, + ZINTERCARD, + zInterCard: ZINTERCARD, + ZINTERSTORE, + zInterStore: ZINTERSTORE, + ZLEXCOUNT, + zLexCount: ZLEXCOUNT, + ZMSCORE, + zmScore: ZMSCORE, + ZRANDMEMBER_COUNT_WITHSCORES, + zRandMemberCountWithScores: ZRANDMEMBER_COUNT_WITHSCORES, + ZRANDMEMBER_COUNT, + zRandMemberCount: ZRANDMEMBER_COUNT, + ZRANDMEMBER, + zRandMember: ZRANDMEMBER, + ZRANGE, + zRange: ZRANGE, + ZRANK, + zRank: ZRANK, + ZREM, + zRem: ZREM, + ZREVRANK, + zRevRank: ZREVRANK, + ZSCAN, + zScan: ZSCAN, + ZSCORE, + zScore: ZSCORE +} as const satisfies Record; diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 30709703153..898d3833a52 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -63,3 +63,7 @@ export class ErrorReply extends Error { this.stack = undefined; } } + +export class SimpleError extends ErrorReply {} + +export class BlobError extends ErrorReply {} diff --git a/packages/client/lib/lua-script.ts b/packages/client/lib/lua-script.ts index da19417ec25..8d20735a5f0 100644 --- a/packages/client/lib/lua-script.ts +++ b/packages/client/lib/lua-script.ts @@ -1,22 +1,22 @@ import { createHash } from 'crypto'; -import { RedisCommand } from './commands'; +import { Command } from './RESP/types'; -export interface RedisScriptConfig extends RedisCommand { - SCRIPT: string; - NUMBER_OF_KEYS?: number; +export type RedisScriptConfig = Command & { + SCRIPT: string | Buffer; + NUMBER_OF_KEYS?: number; } export interface SHA1 { - SHA1: string; + SHA1: string; } export function defineScript(script: S): S & SHA1 { - return { - ...script, - SHA1: scriptSha1(script.SCRIPT) - }; + return { + ...script, + SHA1: scriptSha1(script.SCRIPT) + }; } -export function scriptSha1(script: string): string { - return createHash('sha1').update(script).digest('hex'); +export function scriptSha1(script: RedisScriptConfig['SCRIPT']): string { + return createHash('sha1').update(script).digest('hex'); } diff --git a/packages/client/lib/multi-command.ts b/packages/client/lib/multi-command.ts index 08f23ffa454..2c3f7f7ee7b 100644 --- a/packages/client/lib/multi-command.ts +++ b/packages/client/lib/multi-command.ts @@ -1,87 +1,60 @@ -import { fCallArguments } from './commander'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunction, RedisScript } from './commands'; +import { Command, CommandArguments, RedisScript, TransformReply } from './RESP/types'; import { WatchError } from './errors'; export interface RedisMultiQueuedCommand { - args: RedisCommandArguments; - transformReply?: RedisCommand['transformReply']; + args: CommandArguments; + transformReply?: TransformReply; } export default class RedisMultiCommand { - static generateChainId(): symbol { - return Symbol('RedisMultiCommand Chain Id'); + static generateChainId(): symbol { + return Symbol('RedisMultiCommand Chain Id'); + } + + readonly queue: Array = []; + + readonly scriptsInUse = new Set(); + + addCommand(args: CommandArguments, transformReply?: TransformReply) { + this.queue.push({ + args, + transformReply + }); + return this; + } + + addScript(script: RedisScript, args: CommandArguments, transformReply?: TransformReply) { + const redisArgs: CommandArguments = []; + if (this.scriptsInUse.has(script.SHA1)) { + redisArgs.push('EVALSHA', script.SHA1); + } else { + this.scriptsInUse.add(script.SHA1); + redisArgs.push('EVAL', script.SCRIPT); } - readonly queue: Array = []; - - readonly scriptsInUse = new Set(); - - addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): void { - this.queue.push({ - args, - transformReply - }); + if (script.NUMBER_OF_KEYS !== undefined) { + redisArgs.push(script.NUMBER_OF_KEYS.toString()); } - addFunction(name: string, fn: RedisFunction, args: Array): RedisCommandArguments { - const transformedArguments = fCallArguments( - name, - fn, - fn.transformArguments(...args) - ); - this.queue.push({ - args: transformedArguments, - transformReply: fn.transformReply - }); - return transformedArguments; - } - - addScript(script: RedisScript, args: Array): RedisCommandArguments { - const transformedArguments: RedisCommandArguments = []; - if (this.scriptsInUse.has(script.SHA1)) { - transformedArguments.push( - 'EVALSHA', - script.SHA1 - ); - } else { - this.scriptsInUse.add(script.SHA1); - transformedArguments.push( - 'EVAL', - script.SCRIPT - ); - } - - if (script.NUMBER_OF_KEYS !== undefined) { - transformedArguments.push(script.NUMBER_OF_KEYS.toString()); - } + redisArgs.push(...args); + redisArgs.preserve = args.preserve; - const scriptArguments = script.transformArguments(...args); - transformedArguments.push(...scriptArguments); - if (scriptArguments.preserve) { - transformedArguments.preserve = scriptArguments.preserve; - } - - this.addCommand( - transformedArguments, - script.transformReply - ); - - return transformedArguments; + return this.addCommand(redisArgs, transformReply); + } + + handleExecReplies(rawReplies: Array): Array { + const execReply = rawReplies[rawReplies.length - 1] as (null | Array); + if (execReply === null) { + throw new WatchError(); } - handleExecReplies(rawReplies: Array): Array { - const execReply = rawReplies[rawReplies.length - 1] as (null | Array); - if (execReply === null) { - throw new WatchError(); - } + return this.transformReplies(execReply); + } - return this.transformReplies(execReply); - } - - transformReplies(rawReplies: Array): Array { - return rawReplies.map((reply, i) => { - const { transformReply, args } = this.queue[i]; - return transformReply ? transformReply(reply, args.preserve) : reply; - }); - } + transformReplies(rawReplies: Array): Array { + return rawReplies.map((reply, i) => { + const { transformReply, args } = this.queue[i]; + return transformReply ? transformReply(reply, args.preserve) : reply; + }); + } } diff --git a/packages/client/package.json b/packages/client/package.json index 5f5793251dc..806c90af218 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.7", + "version": "2.0.0-next.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -21,19 +21,19 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", - "@types/sinon": "^10.0.13", + "@types/node": "^18.16.1", + "@types/sinon": "^10.0.14", "@types/yallist": "^4.0.1", - "@typescript-eslint/eslint-plugin": "^5.53.0", - "@typescript-eslint/parser": "^5.53.0", - "eslint": "^8.34.0", + "@typescript-eslint/eslint-plugin": "^5.59.1", + "@typescript-eslint/parser": "^5.59.1", + "eslint": "^8.39.0", "nyc": "^15.1.0", - "release-it": "^15.6.0", - "sinon": "^15.0.1", + "release-it": "^15.10.1", + "sinon": "^15.0.4", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "engines": { "node": ">=14" diff --git a/packages/graph/package.json b/packages/graph/package.json index bbd5dd778d9..c37f956ffb2 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.16.1", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "repository": { "type": "git", @@ -37,5 +37,4 @@ "keywords": [ "redis", "RedisGraph" - ] -} + ]} diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index 21bad09568b..107f9dd9750 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const FIRST_KEY_INDEX = 1; @@ -17,7 +17,7 @@ export function transformArguments(key: string, options?: GetOptions): RedisComm let args: RedisCommandArguments = ['JSON.GET', key]; if (options?.path) { - args = pushVerdictArguments(args, options.path); + args = pushVariadicArguments(args, options.path); } if (options?.INDENT) { diff --git a/packages/json/package.json b/packages/json/package.json index b2f0ec9dbe2..6f30dc16779 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.16.1", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "repository": { "type": "git", diff --git a/.release-it.json b/packages/redis/.release-it.json similarity index 100% rename from .release-it.json rename to packages/redis/.release-it.json diff --git a/packages/redis/README.md b/packages/redis/README.md new file mode 100644 index 00000000000..0d758d1088c --- /dev/null +++ b/packages/redis/README.md @@ -0,0 +1,359 @@ +# Node-Redis + +[![Tests](https://img.shields.io/github/actions/workflow/status/redis/node-redis/tests.yml?branch=master)](https://github.com/redis/node-redis/actions/workflows/tests.yml) +[![Coverage](https://codecov.io/gh/redis/node-redis/branch/master/graph/badge.svg?token=xcfqHhJC37)](https://codecov.io/gh/redis/node-redis) +[![License](https://img.shields.io/github/license/redis/node-redis.svg)](https://github.com/redis/node-redis/blob/master/LICENSE) + +[![Discord](https://img.shields.io/discord/697882427875393627.svg?style=social&logo=discord)](https://discord.gg/redis) +[![Twitch](https://img.shields.io/twitch/status/redisinc?style=social)](https://www.twitch.tv/redisinc) +[![YouTube](https://img.shields.io/youtube/channel/views/UCD78lHSwYqMlyetR0_P4Vig?style=social)](https://www.youtube.com/redisinc) +[![Twitter](https://img.shields.io/twitter/follow/redisinc?style=social)](https://twitter.com/redisinc) + +node-redis is a modern, high performance [Redis](https://redis.io) client for Node.js. + +## Installation + +Start a redis via docker: + +``` bash +docker run -p 6379:6379 -it redis/redis-stack-server:latest +``` + +To install node-redis, simply: + +```bash +npm install redis +``` + +> :warning: The new interface is clean and cool, but if you have an existing codebase, you'll want to read the [migration guide](./docs/v3-to-v4.md). + +Looking for a high-level library to handle object mapping? See [redis-om-node](https://github.com/redis/redis-om-node)! + +## Usage + +### Basic Example + +```typescript +import { createClient } from 'redis'; + +const client = createClient(); + +client.on('error', err => console.log('Redis Client Error', err)); + +await client.connect(); + +await client.set('key', 'value'); +const value = await client.get('key'); +await client.disconnect(); +``` + +The above code connects to localhost on port 6379. To connect to a different host or port, use a connection string in the format `redis[s]://[[username][:password]@][host][:port][/db-number]`: + +```typescript +createClient({ + url: 'redis://alice:foobared@awesome.redis.server:6380' +}); +``` + +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). + +To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean. `client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it isn't (for example when the client is still connecting or reconnecting after a network error). + +### Redis Commands + +There is built-in support for all of the [out-of-the-box Redis commands](https://redis.io/commands). They are exposed using the raw Redis command names (`HSET`, `HGETALL`, etc.) and a friendlier camel-cased version (`hSet`, `hGetAll`, etc.): + +```typescript +// raw Redis commands +await client.HSET('key', 'field', 'value'); +await client.HGETALL('key'); + +// friendly JavaScript commands +await client.hSet('key', 'field', 'value'); +await client.hGetAll('key'); +``` + +Modifiers to commands are specified using a JavaScript object: + +```typescript +await client.set('key', 'value', { + expiration: { + type: 'EX', + value: 10 + }, + condition: 'NX' +}); +``` + +Replies will be transformed into useful data structures: + +```typescript +await client.hGetAll('key'); // { field1: 'value1', field2: 'value2' } +await client.hVals('key'); // ['value1', 'value2'] +``` + +`Buffer`s are supported as well: + +```typescript +await client.hSet('key', 'field', Buffer.from('value')); // 'OK' +await client.withFlags({ + [TYPES.BLOB_STRING]: Buffer +}).hGetAll('key'); // { field: } +``` + +### Unsupported Redis Commands + +If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`: + +```typescript +await client.sendCommand(['SET', 'key', 'value', 'NX']); // 'OK' + +await client.sendCommand(['HGETALL', 'key']); // ['key1', 'field1', 'key2', 'field2'] +``` + +### Transactions (Multi/Exec) + +Start a [transaction](https://redis.io/topics/transactions) by calling `.multi()`, then chaining your commands. When you're done, call `.exec()` and you'll get an array back with your results: + +```typescript +await client.set('another-key', 'another-value'); + +const [setKeyReply, otherKeyValue] = await client + .multi() + .set('key', 'value') + .get('another-key') + .exec(); // ['OK', 'another-value'] +``` + +You can also [watch](https://redis.io/topics/transactions#optimistic-locking-using-check-and-set) keys by calling `.watch()`. Your transaction will abort if any of the watched keys change. + +To dig deeper into transactions, check out the [Isolated Execution Guide](./docs/isolated-execution.md). + +### Blocking Commands + +Any command can be run on a new connection by specifying the `isolated` option. The newly created connection is closed when the command's `Promise` is fulfilled. + +This pattern works especially well for blocking commands—such as `BLPOP` and `BLMOVE`: + +```typescript +import { commandOptions } from 'redis'; + +const blPopPromise = client.isolated().blPop( + 'key', + 0 +); + +await client.lPush('key', ['1', '2']); + +await blPopPromise; // '2' +``` + +To learn more about isolated execution, check out the [guide](./docs/isolated-execution.md). + +### Pub/Sub + +See the [Pub/Sub overview](./docs/pub-sub.md). + +### Scan Iterator + +[`SCAN`](https://redis.io/commands/scan) results can be looped over using [async iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator): + +```typescript +for await (const key of client.scanIterator()) { + // use the key! + await client.get(key); +} +``` + +This works with `HSCAN`, `SSCAN`, and `ZSCAN` too: + +```typescript +for await (const { field, value } of client.hScanIterator('hash')) {} +for await (const member of client.sScanIterator('set')) {} +for await (const { score, value } of client.zScanIterator('sorted-set')) {} +``` + +You can override the default options by providing a configuration object: + +```typescript +client.scanIterator({ + TYPE: 'string', // `SCAN` only + MATCH: 'patter*', + COUNT: 100 +}); +``` + +### [Programmability](https://redis.io/docs/manual/programmability/) + +Redis provides a programming interface allowing code execution on the redis server. + +#### [Functions](https://redis.io/docs/manual/programmability/functions-intro/) + +The following example retrieves a key in redis, returning the value of the key, incremented by an integer. For example, if your key _foo_ has the value _17_ and we run `add('foo', 25)`, it returns the answer to Life, the Universe and Everything. + +```lua +#!lua name=library + +redis.register_function { + function_name = 'add', + callback = function(keys, args) return redis.call('GET', keys[1]) + args[1] end, + flags = { 'no-writes' } +} +``` + +Here is the same example, but in a format that can be pasted into the `redis-cli`. + +``` +FUNCTION LOAD "#!lua name=library\nredis.register_function{function_name=\"add\", callback=function(keys, args) return redis.call('GET', keys[1])+args[1] end, flags={\"no-writes\"}}" +``` + +Load the prior redis function on the _redis server_ before running the example below. + +```typescript +import { createClient } from 'redis'; + +const client = createClient({ + functions: { + library: { + add: { + NUMBER_OF_KEYS: 1, + transformArguments(key: string, toAdd: number): Array { + return [key, toAdd.toString()]; + }, + transformReply(reply: number): number { + return reply; + } + } + } + } +}); + +await client.connect(); + +await client.set('key', '1'); +await client.library.add('key', 2); // 3 +``` + +#### [Lua Scripts](https://redis.io/docs/manual/programmability/eval-intro/) + +The following is an end-to-end example of the prior concept. + +```typescript +import { createClient, defineScript } from 'redis'; + +const client = createClient({ + scripts: { + add: defineScript({ + NUMBER_OF_KEYS: 1, + SCRIPT: + 'return redis.call("GET", KEYS[1]) + ARGV[1];', + transformArguments(key: string, toAdd: number): Array { + return [key, toAdd.toString()]; + }, + transformReply(reply: number): number { + return reply; + } + }) + } +}); + +await client.connect(); + +await client.set('key', '1'); +await client.add('key', 2); // 3 +``` + +### Disconnecting + +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 +const [ping, get, quit] = await Promise.all([ + client.ping(), + client.get('key'), + client.quit() +]); // ['PONG', null, 'OK'] + +try { + await client.get('key'); +} catch (err) { + // ClosedClient Error +} +``` + +#### `.disconnect()` + +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. + +```typescript +await client.disconnect(); +``` + +### Auto-Pipelining + +Node Redis will automatically pipeline requests that are made during the same "tick". + +```typescript +client.set('Tm9kZSBSZWRpcw==', 'users:1'); +client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw=='); +``` + +Of course, if you don't do something with your Promises you're certain to get [unhandled Promise exceptions](https://nodejs.org/api/process.html#process_event_unhandledrejection). To take advantage of auto-pipelining and handle your Promises, use `Promise.all()`. + +```typescript +await Promise.all([ + client.set('Tm9kZSBSZWRpcw==', 'users:1'), + client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==') +]); +``` + +### Clustering + +Check out the [Clustering Guide](./docs/clustering.md) when using Node Redis to connect to a Redis Cluster. + +### Events + +The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes: + +| Name | When | Listener arguments | +|-------------------------|------------------------------------------------------------------------------------|------------------------------------------------------------| +| `connect` | Initiating a connection to the server | *No arguments* | +| `ready` | Client is ready to use | *No arguments* | +| `end` | Connection has been closed (via `.quit()` or `.disconnect()`) | *No arguments* | +| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` | +| `reconnecting` | Client is trying to reconnect to the server | *No arguments* | +| `sharded-channel-moved` | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | See [here](./docs/pub-sub.md#sharded-channel-moved-event) | + +> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and an `error` occurs, that error will be thrown and the Node.js process will exit. See the [`EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details. + +> The client will not emit [any other events](./docs/v3-to-v4.md#all-the-removed-events) beyond those listed above. + +## Supported Redis versions + +Node Redis is supported with the following versions of Redis: + +| Version | Supported | +|---------|--------------------| +| 7.0.z | :heavy_check_mark: | +| 6.2.z | :heavy_check_mark: | +| 6.0.z | :heavy_check_mark: | +| 5.0.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 + +If you'd like to contribute, check out the [contributing guide](CONTRIBUTING.md). + +Thank you to all the people who already contributed to Node Redis! + +[![Contributors](https://contrib.rocks/image?repo=redis/node-redis)](https://github.com/redis/node-redis/graphs/contributors) + +## License + +This repository is licensed under the "MIT" license. See [LICENSE](LICENSE). diff --git a/packages/redis/index.ts b/packages/redis/index.ts new file mode 100644 index 00000000000..58552adf1f3 --- /dev/null +++ b/packages/redis/index.ts @@ -0,0 +1,77 @@ +import { + RedisModules, + RedisFunctions, + RedisScripts, + createClient as _createClient, + RedisClientOptions, + RedisClientType as _RedisClientType, + createCluster as _createCluster, + RedisClusterOptions, + RedisClusterType as _RedisClusterType +} from '@redis/client'; +import RedisBloomModules from '@redis/bloom'; +import RedisGraph from '@redis/graph'; +import RedisJSON from '@redis/json'; +import RediSearch from '@redis/search'; +import RedisTimeSeries from '@redis/time-series'; + +export * from '@redis/client'; +export * from '@redis/bloom'; +export * from '@redis/graph'; +export * from '@redis/json'; +export * from '@redis/search'; +export * from '@redis/time-series'; + +const modules = { + ...RedisBloomModules, + graph: RedisGraph, + json: RedisJSON, + ft: RediSearch, + ts: RedisTimeSeries +}; + +export type RedisDefaultModules = typeof modules; + +export type RedisClientType< + M extends RedisModules = RedisDefaultModules, + F extends RedisFunctions = Record, + S extends RedisScripts = Record +> = _RedisClientType; + +export function createClient< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +>( + options?: RedisClientOptions +): _RedisClientType { + return _createClient({ + ...options, + modules: { + ...modules, + ...(options?.modules as M) + } + }); +} + +export type RedisClusterType< + M extends RedisModules = RedisDefaultModules, + F extends RedisFunctions = Record, + S extends RedisScripts = Record +> = _RedisClusterType; + +export function createCluster< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts +>( + options: RedisClusterOptions +): RedisClusterType { + return _createCluster({ + ...options, + modules: { + ...modules, + ...(options?.modules as M) + } + }); +} diff --git a/packages/redis/package.json b/packages/redis/package.json new file mode 100644 index 00000000000..35fc2eb028b --- /dev/null +++ b/packages/redis/package.json @@ -0,0 +1,37 @@ +{ + "name": "redis", + "description": "A modern, high performance Redis client", + "version": "4.6.6", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/" + ], + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.7", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.2", + "@redis/time-series": "1.0.4" + }, + "devDependencies": { + "release-it": "^15.9.3", + "typescript": "^5.0.2" + }, + "repository": { + "type": "git", + "url": "git://github.com/redis/node-redis.git" + }, + "bugs": { + "url": "https://github.com/redis/node-redis/issues" + }, + "homepage": "https://github.com/redis/node-redis", + "keywords": [ + "redis" + ] +} diff --git a/tsconfig.json b/packages/redis/tsconfig.json similarity index 68% rename from tsconfig.json rename to packages/redis/tsconfig.json index 285b7ff0a97..50da0ba733a 100644 --- a/tsconfig.json +++ b/packages/redis/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "./tsconfig.base.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist" }, diff --git a/packages/search/lib/commands/AGGREGATE.ts b/packages/search/lib/commands/AGGREGATE.ts index c32d20b0b1c..fb831f54bc0 100644 --- a/packages/search/lib/commands/AGGREGATE.ts +++ b/packages/search/lib/commands/AGGREGATE.ts @@ -1,5 +1,5 @@ import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArgument, transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArgument, transformTuplesReply } from '@redis/client/dist/lib/commands/generic-transformers'; import { Params, PropertyName, pushArgumentsWithLength, pushParamsArgs, pushSortByArguments, SortByProperty } from '.'; export enum AggregateSteps { @@ -170,7 +170,7 @@ export function pushAggregatehOptions( if (!step.properties) { args.push('0'); } else { - pushVerdictArgument(args, step.properties); + pushVariadicArgument(args, step.properties); } if (Array.isArray(step.REDUCE)) { diff --git a/packages/search/lib/commands/CREATE.ts b/packages/search/lib/commands/CREATE.ts index 21662c28d7d..32440ede38e 100644 --- a/packages/search/lib/commands/CREATE.ts +++ b/packages/search/lib/commands/CREATE.ts @@ -1,4 +1,4 @@ -import { pushOptionalVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushOptionalVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisSearchLanguages, PropertyName, RediSearchSchema, pushSchema } from '.'; interface CreateOptions { @@ -27,7 +27,7 @@ export function transformArguments(index: string, schema: RediSearchSchema, opti args.push('ON', options.ON); } - pushOptionalVerdictArgument(args, 'PREFIX', options?.PREFIX); + pushOptionalVariadicArgument(args, 'PREFIX', options?.PREFIX); if (options?.FILTER) { args.push('FILTER', options.FILTER); @@ -81,7 +81,7 @@ export function transformArguments(index: string, schema: RediSearchSchema, opti args.push('SKIPINITIALSCAN'); } - pushOptionalVerdictArgument(args, 'STOPWORDS', options?.STOPWORDS); + pushOptionalVariadicArgument(args, 'STOPWORDS', options?.STOPWORDS); args.push('SCHEMA'); pushSchema(args, schema); diff --git a/packages/search/lib/commands/DICTADD.ts b/packages/search/lib/commands/DICTADD.ts index 60af11fd41f..2a8d9c522bc 100644 --- a/packages/search/lib/commands/DICTADD.ts +++ b/packages/search/lib/commands/DICTADD.ts @@ -1,8 +1,8 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { - return pushVerdictArguments(['FT.DICTADD', dictionary], term); + return pushVariadicArguments(['FT.DICTADD', dictionary], term); } export declare function transformReply(): number; diff --git a/packages/search/lib/commands/DICTDEL.ts b/packages/search/lib/commands/DICTDEL.ts index a1b728f1926..8a8513f5595 100644 --- a/packages/search/lib/commands/DICTDEL.ts +++ b/packages/search/lib/commands/DICTDEL.ts @@ -1,8 +1,8 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export function transformArguments(dictionary: string, term: string | Array): RedisCommandArguments { - return pushVerdictArguments(['FT.DICTDEL', dictionary], term); + return pushVariadicArguments(['FT.DICTDEL', dictionary], term); } export declare function transformReply(): number; diff --git a/packages/search/lib/commands/SYNUPDATE.ts b/packages/search/lib/commands/SYNUPDATE.ts index 3384ea59d94..80582759c47 100644 --- a/packages/search/lib/commands/SYNUPDATE.ts +++ b/packages/search/lib/commands/SYNUPDATE.ts @@ -1,4 +1,4 @@ -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; interface SynUpdateOptions { @@ -17,7 +17,7 @@ export function transformArguments( args.push('SKIPINITIALSCAN'); } - return pushVerdictArguments(args, terms); + return pushVariadicArguments(args, terms); } export declare function transformReply(): 'OK'; diff --git a/packages/search/lib/commands/index.ts b/packages/search/lib/commands/index.ts index d56db7bdbbe..f3fb16d7021 100644 --- a/packages/search/lib/commands/index.ts +++ b/packages/search/lib/commands/index.ts @@ -32,7 +32,7 @@ import * as SYNDUMP from './SYNDUMP'; import * as SYNUPDATE from './SYNUPDATE'; import * as TAGVALS from './TAGVALS'; import { RedisCommandArgument, RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushOptionalVerdictArgument, pushVerdictArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushOptionalVariadicArgument, pushVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { SearchOptions } from './SEARCH'; export default { @@ -411,9 +411,9 @@ export function pushSearchOptions( // args.push('WITHPAYLOADS'); // } - pushOptionalVerdictArgument(args, 'INKEYS', options?.INKEYS); - pushOptionalVerdictArgument(args, 'INFIELDS', options?.INFIELDS); - pushOptionalVerdictArgument(args, 'RETURN', options?.RETURN); + pushOptionalVariadicArgument(args, 'INKEYS', options?.INKEYS); + pushOptionalVariadicArgument(args, 'INFIELDS', options?.INFIELDS); + pushOptionalVariadicArgument(args, 'RETURN', options?.RETURN); if (options?.SUMMARIZE) { args.push('SUMMARIZE'); @@ -421,7 +421,7 @@ export function pushSearchOptions( if (typeof options.SUMMARIZE === 'object') { if (options.SUMMARIZE.FIELDS) { args.push('FIELDS'); - pushVerdictArgument(args, options.SUMMARIZE.FIELDS); + pushVariadicArgument(args, options.SUMMARIZE.FIELDS); } if (options.SUMMARIZE.FRAGS) { @@ -444,7 +444,7 @@ export function pushSearchOptions( if (typeof options.HIGHLIGHT === 'object') { if (options.HIGHLIGHT.FIELDS) { args.push('FIELDS'); - pushVerdictArgument(args, options.HIGHLIGHT.FIELDS); + pushVariadicArgument(args, options.HIGHLIGHT.FIELDS); } if (options.HIGHLIGHT.TAGS) { diff --git a/packages/search/package.json b/packages/search/package.json index 61154daa014..27e80335c95 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.16.1", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "repository": { "type": "git", @@ -37,5 +37,4 @@ "keywords": [ "redis", "RediSearch" - ] -} + ]} diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index a7e1c610eee..d750b7d5121 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -2,259 +2,263 @@ import { createConnection } from 'net'; import { once } from 'events'; import RedisClient from '@redis/client/dist/lib/client'; import { promiseTimeout } from '@redis/client/dist/lib/utils'; -import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; +// import { ClusterSlotsReply } from '@redis/client/dist/lib/commands/CLUSTER_SLOTS'; import * as path from 'path'; import { promisify } from 'util'; import { exec } from 'child_process'; const execAsync = promisify(exec); interface ErrorWithCode extends Error { - code: string; + code: string; } async function isPortAvailable(port: number): Promise { - try { - const socket = createConnection({ port }); - await once(socket, 'connect'); - socket.end(); - } catch (err) { - if (err instanceof Error && (err as ErrorWithCode).code === 'ECONNREFUSED') { - return true; - } + try { + const socket = createConnection({ port }); + await once(socket, 'connect'); + socket.end(); + } catch (err) { + if (err instanceof Error && (err as ErrorWithCode).code === 'ECONNREFUSED') { + return true; } + } - return false; + return false; } -const portIterator = (async function*(): AsyncIterableIterator { - for (let i = 6379; i < 65535; i++) { - if (await isPortAvailable(i)) { - yield i; - } +const portIterator = (async function* (): AsyncIterableIterator { + for (let i = 6379; i < 65535; i++) { + if (await isPortAvailable(i)) { + yield i; } + } - throw new Error('All ports are in use'); + throw new Error('All ports are in use'); })(); export interface RedisServerDockerConfig { - image: string; - version: string; + image: string; + version: string; } export interface RedisServerDocker { - port: number; - dockerId: string; + port: number; + dockerId: string; } // ".." cause it'll be in `./dist` const DOCKER_FODLER_PATH = path.join(__dirname, '../docker'); async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfig, serverArguments: Array): Promise { - const port = (await portIterator.next()).value, - { stdout, stderr } = await execAsync( - 'docker run -d --network host $(' + - `docker build ${DOCKER_FODLER_PATH} -q ` + - `--build-arg IMAGE=${image}:${version} ` + - `--build-arg REDIS_ARGUMENTS="--save '' --port ${port.toString()} ${serverArguments.join(' ')}"` + - ')' - ); + const port = (await portIterator.next()).value, + { stdout, stderr } = await execAsync( + 'docker run -d --network host $(' + + `docker build ${DOCKER_FODLER_PATH} -q ` + + `--build-arg IMAGE=${image}:${version} ` + + `--build-arg REDIS_ARGUMENTS="--save '' --port ${port.toString()} ${serverArguments.join(' ')}"` + + ')' + ); - if (!stdout) { - throw new Error(`docker run error - ${stderr}`); - } + if (!stdout) { + throw new Error(`docker run error - ${stderr}`); + } - while (await isPortAvailable(port)) { - await promiseTimeout(50); - } + while (await isPortAvailable(port)) { + await promiseTimeout(50); + } - return { - port, - dockerId: stdout.trim() - }; + return { + port, + dockerId: stdout.trim() + }; } const RUNNING_SERVERS = new Map, ReturnType>(); export function spawnRedisServer(dockerConfig: RedisServerDockerConfig, serverArguments: Array): Promise { - const runningServer = RUNNING_SERVERS.get(serverArguments); - if (runningServer) { - return runningServer; - } - - const dockerPromise = spawnRedisServerDocker(dockerConfig, serverArguments); - RUNNING_SERVERS.set(serverArguments, dockerPromise); - return dockerPromise; + const runningServer = RUNNING_SERVERS.get(serverArguments); + if (runningServer) { + return runningServer; + } + + const dockerPromise = spawnRedisServerDocker(dockerConfig, serverArguments); + RUNNING_SERVERS.set(serverArguments, dockerPromise); + return dockerPromise; } async function dockerRemove(dockerId: string): Promise { - const { stderr } = await execAsync(`docker rm -f ${dockerId}`); - if (stderr) { - throw new Error(`docker rm error - ${stderr}`); - } + const { stderr } = await execAsync(`docker rm -f ${dockerId}`); + if (stderr) { + throw new Error(`docker rm error - ${stderr}`); + } } after(() => { - return Promise.all( - [...RUNNING_SERVERS.values()].map(async dockerPromise => - await dockerRemove((await dockerPromise).dockerId) - ) - ); + return Promise.all( + [...RUNNING_SERVERS.values()].map(async dockerPromise => + await dockerRemove((await dockerPromise).dockerId) + ) + ); }); export interface RedisClusterDockersConfig extends RedisServerDockerConfig { - numberOfMasters?: number; - numberOfReplicas?: number; + numberOfMasters?: number; + numberOfReplicas?: number; } async function spawnRedisClusterNodeDockers( - dockersConfig: RedisClusterDockersConfig, - serverArguments: Array, - fromSlot: number, - toSlot: number + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array, + fromSlot: number, + toSlot: number ) { - const range: Array = []; - for (let i = fromSlot; i < toSlot; i++) { - range.push(i); - } - - const master = await spawnRedisClusterNodeDocker( - dockersConfig, - serverArguments - ); + const range: Array = []; + for (let i = fromSlot; i < toSlot; i++) { + range.push(i); + } + + const master = await spawnRedisClusterNodeDocker( + dockersConfig, + serverArguments + ); + + await master.client.clusterAddSlots(range); + + if (!dockersConfig.numberOfReplicas) return [master]; + + const replicasPromises: Array> = []; + for (let i = 0; i < (dockersConfig.numberOfReplicas ?? 0); i++) { + replicasPromises.push( + spawnRedisClusterNodeDocker(dockersConfig, [ + ...serverArguments, + '--cluster-enabled', + 'yes', + '--cluster-node-timeout', + '5000' + ]).then(async replica => { + await replica.client.clusterMeet('127.0.0.1', master.docker.port); + + while ((await replica.client.clusterSlots()).length === 0) { + await promiseTimeout(50); + } - await master.client.clusterAddSlots(range); - - if (!dockersConfig.numberOfReplicas) return [master]; - - const replicasPromises: Array> = []; - for (let i = 0; i < (dockersConfig.numberOfReplicas ?? 0); i++) { - replicasPromises.push( - spawnRedisClusterNodeDocker(dockersConfig, [ - ...serverArguments, - '--cluster-enabled', - 'yes', - '--cluster-node-timeout', - '5000' - ]).then(async replica => { - await replica.client.clusterMeet('127.0.0.1', master.docker.port); - - while ((await replica.client.clusterSlots()).length === 0) { - await promiseTimeout(50); - } - - await replica.client.clusterReplicate( - await master.client.clusterMyId() - ); - - return replica; - }) + await replica.client.clusterReplicate( + await master.client.clusterMyId() ); - } - return [ - master, - ...await Promise.all(replicasPromises) - ]; + return replica; + }) + ); + } + + return [ + master, + ...await Promise.all(replicasPromises) + ]; } async function spawnRedisClusterNodeDocker( - dockersConfig: RedisClusterDockersConfig, - serverArguments: Array + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array ) { - const docker = await spawnRedisServerDocker(dockersConfig, [ - ...serverArguments, - '--cluster-enabled', - 'yes', - '--cluster-node-timeout', - '5000' - ]), - client = RedisClient.create({ - socket: { - port: docker.port - } - }); - - await client.connect(); - - return { - docker, - client - }; + const docker = await spawnRedisServerDocker(dockersConfig, [ + ...serverArguments, + '--cluster-enabled', + 'yes', + '--cluster-node-timeout', + '5000' + ]), + client = RedisClient.create({ + socket: { + port: docker.port + } + }); + + await client.connect(); + + return { + docker, + client + }; } const SLOTS = 16384; async function spawnRedisClusterDockers( - dockersConfig: RedisClusterDockersConfig, - serverArguments: Array + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array ): Promise> { - const numberOfMasters = dockersConfig.numberOfMasters ?? 2, - slotsPerNode = Math.floor(SLOTS / numberOfMasters), - spawnPromises: Array> = []; - for (let i = 0; i < numberOfMasters; i++) { - const fromSlot = i * slotsPerNode, - toSlot = i === numberOfMasters - 1 ? SLOTS : fromSlot + slotsPerNode; - spawnPromises.push( - spawnRedisClusterNodeDockers( - dockersConfig, - serverArguments, - fromSlot, - toSlot - ) - ); - } + const numberOfMasters = dockersConfig.numberOfMasters ?? 2, + slotsPerNode = Math.floor(SLOTS / numberOfMasters), + spawnPromises: Array> = []; + for (let i = 0; i < numberOfMasters; i++) { + const fromSlot = i * slotsPerNode, + toSlot = i === numberOfMasters - 1 ? SLOTS : fromSlot + slotsPerNode; + spawnPromises.push( + spawnRedisClusterNodeDockers( + dockersConfig, + serverArguments, + fromSlot, + toSlot + ) + ); + } - const nodes = (await Promise.all(spawnPromises)).flat(), - meetPromises: Array> = []; - for (let i = 1; i < nodes.length; i++) { - meetPromises.push( - nodes[i].client.clusterMeet('127.0.0.1', nodes[0].docker.port) - ); - } + const nodes = (await Promise.all(spawnPromises)).flat(), + meetPromises: Array> = []; + for (let i = 1; i < nodes.length; i++) { + meetPromises.push( + nodes[i].client.clusterMeet('127.0.0.1', nodes[0].docker.port) + ); + } - await Promise.all(meetPromises); + await Promise.all(meetPromises); - await Promise.all( - nodes.map(async ({ client }) => { - while (totalNodes(await client.clusterSlots()) !== nodes.length) { - await promiseTimeout(50); - } - - return client.disconnect(); - }) - ); + await Promise.all( + nodes.map(async ({ client }) => { + while ( + totalNodes(await client.clusterSlots()) !== nodes.length || + !(await client.sendCommand(['CLUSTER', 'INFO'])).startsWith('cluster_state:ok') // TODO + ) { + await promiseTimeout(50); + } + + return client.disconnect(); + }) + ); - return nodes.map(({ docker }) => docker); + return nodes.map(({ docker }) => docker); } -function totalNodes(slots: ClusterSlotsReply) { - let total = slots.length; - for (const slot of slots) { - total += slot.replicas.length; - } +// TODO: type ClusterSlotsReply +function totalNodes(slots: any) { + let total = slots.length; + for (const slot of slots) { + total += slot.replicas.length; + } - return total; + return total; } const RUNNING_CLUSTERS = new Map, ReturnType>(); export function spawnRedisCluster(dockersConfig: RedisClusterDockersConfig, serverArguments: Array): Promise> { - const runningCluster = RUNNING_CLUSTERS.get(serverArguments); - if (runningCluster) { - return runningCluster; - } - - const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments); - RUNNING_CLUSTERS.set(serverArguments, dockersPromise); - return dockersPromise; + const runningCluster = RUNNING_CLUSTERS.get(serverArguments); + if (runningCluster) { + return runningCluster; + } + + const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments); + RUNNING_CLUSTERS.set(serverArguments, dockersPromise); + return dockersPromise; } after(() => { - return Promise.all( - [...RUNNING_CLUSTERS.values()].map(async dockersPromise => { - return Promise.all( - (await dockersPromise).map(({ dockerId }) => dockerRemove(dockerId)) - ); - }) - ); + return Promise.all( + [...RUNNING_CLUSTERS.values()].map(async dockersPromise => { + return Promise.all( + (await dockersPromise).map(({ dockerId }) => dockerRemove(dockerId)) + ); + }) + ); }); diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index b9195c5717a..0fb311bff8b 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -1,226 +1,269 @@ -import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/lib/commands'; -import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; -import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; -import { RedisSocketCommonOptions } from '@redis/client/lib/client/socket'; +// import { RedisModules, RedisFunctions, RedisScripts } from '@redis/client/lib/commands'; +// import RedisClient, { RedisClientOptions, RedisClientType } from '@redis/client/lib/client'; +// import RedisCluster, { RedisClusterOptions, RedisClusterType } from '@redis/client/lib/cluster'; +// import { RedisSocketCommonOptions } from '@redis/client/lib/client/socket'; +import { + RedisModules, + RedisFunctions, + RedisScripts, + RespVersions, + createClient, + RedisClientOptions, + RedisClientType, + createCluster, + RedisClusterOptions, + RedisClusterType +} from '@redis/client/index'; import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; interface TestUtilsConfig { - dockerImageName: string; - dockerImageVersionArgument: string; - defaultDockerVersion?: string; + dockerImageName: string; + dockerImageVersionArgument: string; + defaultDockerVersion?: string; } interface CommonTestOptions { - minimumDockerVersion?: Array; + minimumDockerVersion?: Array; } interface ClientTestOptions< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions > extends CommonTestOptions { - serverArguments: Array; - clientOptions?: Partial, 'socket'> & { socket: RedisSocketCommonOptions }>; - disableClientSetup?: boolean; + serverArguments: Array; + clientOptions?: Partial>; + disableClientSetup?: boolean; } interface ClusterTestOptions< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions > extends CommonTestOptions { - serverArguments: Array; - clusterConfiguration?: Partial>; - numberOfMasters?: number; - numberOfReplicas?: number; + serverArguments: Array; + clusterConfiguration?: Partial>; + numberOfMasters?: number; + numberOfReplicas?: number; +} + +interface AllTestOptions< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions +> { + client: ClientTestOptions; + cluster: ClusterTestOptions; } interface Version { - string: string; - numbers: Array; + string: string; + numbers: Array; } export default class TestUtils { - static #parseVersionNumber(version: string): Array { - if (version === 'latest' || version === 'edge') return [Infinity]; - - const dashIndex = version.indexOf('-'); - return (dashIndex === -1 ? version : version.substring(0, dashIndex)) - .split('.') - .map(x => { - const value = Number(x); - if (Number.isNaN(value)) { - throw new TypeError(`${version} is not a valid redis version`); - } - - return value; - }); - } + static #parseVersionNumber(version: string): Array { + if (version === 'latest' || version === 'edge') return [Infinity]; - static #getVersion(argumentName: string, defaultVersion = 'latest'): Version { - return yargs(hideBin(process.argv)) - .option(argumentName, { - type: 'string', - default: defaultVersion - }) - .coerce(argumentName, (version: string) => { - return { - string: version, - numbers: TestUtils.#parseVersionNumber(version) - }; - }) - .demandOption(argumentName) - .parseSync()[argumentName]; - } + const dashIndex = version.indexOf('-'); + return (dashIndex === -1 ? version : version.substring(0, dashIndex)) + .split('.') + .map(x => { + const value = Number(x); + if (Number.isNaN(value)) { + throw new TypeError(`${version} is not a valid redis version`); + } - readonly #VERSION_NUMBERS: Array; - readonly #DOCKER_IMAGE: RedisServerDockerConfig; + return value; + }); + } - constructor(config: TestUtilsConfig) { - const { string, numbers } = TestUtils.#getVersion(config.dockerImageVersionArgument, config.defaultDockerVersion); - this.#VERSION_NUMBERS = numbers; - this.#DOCKER_IMAGE = { - image: config.dockerImageName, - version: string + static #getVersion(argumentName: string, defaultVersion = 'latest'): Version { + return yargs(hideBin(process.argv)) + .option(argumentName, { + type: 'string', + default: defaultVersion + }) + .coerce(argumentName, (version: string) => { + return { + string: version, + numbers: TestUtils.#parseVersionNumber(version) }; - } + }) + .demandOption(argumentName) + .parseSync()[argumentName]; + } - isVersionGreaterThan(minimumVersion: Array | undefined): boolean { - if (minimumVersion === undefined) return true; + readonly #VERSION_NUMBERS: Array; + readonly #DOCKER_IMAGE: RedisServerDockerConfig; - const lastIndex = Math.min(this.#VERSION_NUMBERS.length, minimumVersion.length) - 1; - for (let i = 0; i < lastIndex; i++) { - if (this.#VERSION_NUMBERS[i] > minimumVersion[i]) { - return true; - } else if (minimumVersion[i] > this.#VERSION_NUMBERS[i]) { - return false; - } - } + constructor(config: TestUtilsConfig) { + const { string, numbers } = TestUtils.#getVersion(config.dockerImageVersionArgument, config.defaultDockerVersion); + this.#VERSION_NUMBERS = numbers; + this.#DOCKER_IMAGE = { + image: config.dockerImageName, + version: string + }; + } + + isVersionGreaterThan(minimumVersion: Array | undefined): boolean { + if (minimumVersion === undefined) return true; - return this.#VERSION_NUMBERS[lastIndex] >= minimumVersion[lastIndex]; + const lastIndex = Math.min(this.#VERSION_NUMBERS.length, minimumVersion.length) - 1; + for (let i = 0; i < lastIndex; i++) { + if (this.#VERSION_NUMBERS[i] > minimumVersion[i]) { + return true; + } else if (minimumVersion[i] > this.#VERSION_NUMBERS[i]) { + return false; + } } - isVersionGreaterThanHook(minimumVersion: Array | undefined): void { - const isVersionGreaterThan = this.isVersionGreaterThan.bind(this); - before(function () { - if (!isVersionGreaterThan(minimumVersion)) { - return this.skip(); - } - }); + return this.#VERSION_NUMBERS[lastIndex] >= minimumVersion[lastIndex]; + } + + isVersionGreaterThanHook(minimumVersion: Array | undefined): void { + const isVersionGreaterThan = this.isVersionGreaterThan.bind(this); + before(function () { + if (!isVersionGreaterThan(minimumVersion)) { + return this.skip(); + } + }); + } + + testWithClient< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions + >( + title: string, + fn: (client: RedisClientType) => unknown, + options: ClientTestOptions + ): void { + let dockerPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(30000); + + dockerPromise = spawnRedisServer(dockerImage, options.serverArguments); + return dockerPromise; + }); } - testWithClient< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >( - title: string, - fn: (client: RedisClientType) => unknown, - options: ClientTestOptions - ): void { - let dockerPromise: ReturnType; - if (this.isVersionGreaterThan(options.minimumDockerVersion)) { - const dockerImage = this.#DOCKER_IMAGE; - before(function () { - this.timeout(30000); - - dockerPromise = spawnRedisServer(dockerImage, options.serverArguments); - return dockerPromise; - }); + it(title, async function () { + if (!dockerPromise) return this.skip(); + + const client = createClient({ + ...options?.clientOptions, + socket: { + ...options?.clientOptions?.socket, + // TODO + // @ts-ignore + port: (await dockerPromise).port } + }); - it(title, async function() { - if (!dockerPromise) return this.skip(); + if (options.disableClientSetup) { + return fn(client); + } - const client = RedisClient.create({ - ...options?.clientOptions, - socket: { - ...options?.clientOptions?.socket, - port: (await dockerPromise).port - } - }); + await client.connect(); - if (options.disableClientSetup) { - return fn(client); - } + try { + await client.flushAll(); + await fn(client); + } finally { + if (client.isOpen) { + await client.flushAll(); + await client.disconnect(); + } + } + }); + } - await client.connect(); + static async #clusterFlushAll< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions + >(cluster: RedisClusterType): Promise { + return Promise.all( + cluster.masters.map(async ({ client }) => { + if (client) { + await (await client).flushAll(); + } + }) + ); + } - try { - await client.flushAll(); - await fn(client); - } finally { - if (client.isOpen) { - await client.flushAll(); - await client.disconnect(); - } - } - }); - } + testWithCluster< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions + >( + title: string, + fn: (cluster: RedisClusterType) => unknown, + options: ClusterTestOptions + ): void { + let dockersPromise: ReturnType; + if (this.isVersionGreaterThan(options.minimumDockerVersion)) { + const dockerImage = this.#DOCKER_IMAGE; + before(function () { + this.timeout(30000); - static async #clusterFlushAll< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >(cluster: RedisClusterType): Promise { - return Promise.all( - cluster.masters.map(async ({ client }) => { - if (client) { - await (await client).flushAll(); - } - }) - ); + dockersPromise = spawnRedisCluster({ + ...dockerImage, + numberOfMasters: options?.numberOfMasters, + numberOfReplicas: options?.numberOfReplicas + }, options.serverArguments); + return dockersPromise; + }); } - testWithCluster< - M extends RedisModules, - F extends RedisFunctions, - S extends RedisScripts - >( - title: string, - fn: (cluster: RedisClusterType) => unknown, - options: ClusterTestOptions - ): void { - let dockersPromise: ReturnType; - if (this.isVersionGreaterThan(options.minimumDockerVersion)) { - const dockerImage = this.#DOCKER_IMAGE; - before(function () { - this.timeout(30000); - - dockersPromise = spawnRedisCluster({ - ...dockerImage, - numberOfMasters: options?.numberOfMasters, - numberOfReplicas: options?.numberOfReplicas - }, options.serverArguments); - return dockersPromise; - }); - } + it(title, async function () { + if (!dockersPromise) return this.skip(); - it(title, async function () { - if (!dockersPromise) return this.skip(); - - const dockers = await dockersPromise, - cluster = RedisCluster.create({ - rootNodes: dockers.map(({ port }) => ({ - socket: { - port - } - })), - minimizeConnections: true, - ...options.clusterConfiguration - }); - - await cluster.connect(); - - try { - await TestUtils.#clusterFlushAll(cluster); - await fn(cluster); - } finally { - await TestUtils.#clusterFlushAll(cluster); - await cluster.disconnect(); + const dockers = await dockersPromise, + cluster = createCluster({ + rootNodes: dockers.map(({ port }) => ({ + socket: { + port } + })), + minimizeConnections: true, + ...options.clusterConfiguration }); - } + + await cluster.connect(); + + try { + await TestUtils.#clusterFlushAll(cluster); + await fn(cluster); + } finally { + await TestUtils.#clusterFlushAll(cluster); + await cluster.disconnect(); + } + }); + } + + testAll< + M extends RedisModules, + F extends RedisFunctions, + S extends RedisScripts, + RESP extends RespVersions + >( + title: string, + fn: (client: RedisClientType | RedisClusterType) => unknown, + options: AllTestOptions + ) { + this.testWithClient(`client.${title}`, fn, options.client); + this.testWithCluster(`cluster.${title}`, fn, options.cluster); + } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index c82ca367301..0ebeea37a26 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,13 +12,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^10.0.1", - "@types/node": "^18.14.1", - "@types/yargs": "^17.0.22", + "@types/node": "^18.16.1", + "@types/yargs": "^17.0.24", "mocha": "^10.2.0", "nyc": "^15.1.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typescript": "^4.9.5", + "typescript": "^5.0.4", "yargs": "^17.7.1" } } diff --git a/packages/time-series/lib/commands/QUERYINDEX.ts b/packages/time-series/lib/commands/QUERYINDEX.ts index 46eb5647040..b2285789185 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.ts @@ -1,11 +1,11 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; import { Filter } from '.'; export const IS_READ_ONLY = true; export function transformArguments(filter: Filter): RedisCommandArguments { - return pushVerdictArguments(['TS.QUERYINDEX'], filter); + return pushVariadicArguments(['TS.QUERYINDEX'], filter); } export declare function transformReply(): Array; diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index 356b0416648..0b0b56d2630 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -20,7 +20,7 @@ import * as MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; import * as MREVRANGE from './MREVRANGE'; import * as MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVerdictArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export default { ADD, @@ -364,7 +364,7 @@ export type Filter = string | Array; export function pushFilterArgument(args: RedisCommandArguments, filter: string | Array): RedisCommandArguments { args.push('FILTER'); - return pushVerdictArguments(args, filter); + return pushVariadicArguments(args, filter); } export interface MRangeOptions extends RangeOptions { @@ -390,7 +390,7 @@ export function pushWithLabelsArgument(args: RedisCommandArguments, selectedLabe args.push('WITHLABELS'); } else { args.push('SELECTED_LABELS'); - args = pushVerdictArguments(args, selectedLabels); + args = pushVariadicArguments(args, selectedLabels); } return args; diff --git a/packages/time-series/package.json b/packages/time-series/package.json index fffad02f62b..095cc72e26b 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,13 +18,13 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@redis/test-utils": "*", - "@types/node": "^18.14.1", + "@types/node": "^18.16.1", "nyc": "^15.1.0", - "release-it": "^15.6.0", + "release-it": "^15.10.1", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", - "typedoc": "^0.23.25", - "typescript": "^4.9.5" + "typedoc": "^0.24.6", + "typescript": "^5.0.4" }, "repository": { "type": "git", @@ -37,5 +37,4 @@ "keywords": [ "redis", "RedisTimeSeries" - ] -} + ]} diff --git a/tsconfig.base.json b/tsconfig.base.json index 68325e51dcc..5824c33a9e6 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/node14/tsconfig.json", + "extends": "@tsconfig/node16/tsconfig.json", "compilerOptions": { "declaration": true, "allowJs": true,