You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-09 00:22:08 +03:00
v4.0.0 (#1730)
* update workflows & README * add .deepsource.toml * fix client.quit, add error events on cluster, fix some "deepsource.io" warnings * Release 4.0.0-rc.1 * add cluster.duplicate, add some tests * fix #1650 - add support for Buffer in some commands, add GET_BUFFER command * fix GET and GET_BUFFER return type * update FAQ * Update invalid code example in README.md (#1654) * Update invalid code example in README.md * Update README.md Co-authored-by: Leibale Eidelman <leibale1998@gmail.com> * fix #1652 * ref #1653 - better types * better types * fix54124793ad
* Update GEOSEARCHSTORE.spec.ts * fix #1660 - add support for client.HSET('key', 'field', 'value') * upgrade dependencies, update README * fix #1659 - add support for db-number in client options url * fix README, remove unused import, downgrade typedoc & typedoc-plugin-markdown * update client-configurations.md * fix README * add CLUSTER_SLOTS, add some tests * fix "createClient with url" test with redis 5 * remove unused imports * Release 4.0.0-rc.2 * add missing semicolon * replace empty "transformReply" functions with typescript "declare" * fix EVAL & EVALSHA, add some tests, npm update * fix #1665 - add ZRANGEBYLEX, ZRANGEBYSCORE, ZRANGEBYSCORE_WITHSCORES * new issue templates * add all COMMAND commands * run COMMAND & COMMAND INFO tests only on redis >6 * Create SECURITY.md * fix #1671 - add support for all client configurations in cluster * ref #1671 - add support for defaults * remove some commands from cluster, npm update, clean code, * lock benny version * fix #1674 - remove `isolationPoolOptions` when creating isolated connection * increase test coverage * update .npmignore * Release 4.0.0-rc.3 * fix README * remove whitespace from LICENSE * use "export { x as y }" instead of import & const * move from "NodeRedis" to "Redis" * fix #1676 * update comments * Auth before select database (#1679) * Auth before select database * fix #1681 Co-authored-by: leibale <leibale1998@gmail.com> * Adds connect-as-acl-user example. (#1684) * Adds connect-as-acl-user example. * Adds blank line at end. * Set to private. * Adds examples folder to npmignore. * Adds Apple .DS_Store file to .gitignore (#1685) * Adds Apple .DS_Store file. * Add .DS_Store to .npmignore too Co-authored-by: Leibale Eidelman <leibale1998@gmail.com> * move examples * clean some tests * clean code * Adds examples table of contents and contribution guidelines. (#1686) * Updated examples to use named functions. (#1687) * Updated examples to user named functions. * Update README.md Co-authored-by: Leibale Eidelman <leibale1998@gmail.com> * update docs, add 6.0.x to the tests matrix, add eslint, npm update, fix some commands, fix some types Co-authored-by: Simon Prickett <simon@crudworks.org> * fix tests with redis 6.0.x * fix ACL GETUSER test * fix client.quit and client.disconnect * fix ACL GETUSER * Adds TypeScript note and corrects a typo. * Fixes a bug in the Scan Iterator section. (#1694) * Made examples use local version. * Add `lua-multi-incr.js` example (#1692) Also fix syntax error in the lua example in the README Closes #1689. * Add(examples): Create an example for blPop & lPush (#1696) * Add(examples): Create an example for blPop & lPush Signed-off-by: Aditya Rastogi <adit.rastogi2014@gmail.com> * Update(examples): fix case, add timeout, update readme Signed-off-by: Aditya Rastogi <adit.rastogi2014@gmail.com> Closes #1693. * Add command-with-modifiers.js example (#1695) * Adds TypeScript note and corrects a typo. * Adds command-with-modifiers example. (redis#1688) * Adds command-with-modifiers example. (redis#1688) * Adds command-with-modifiers example. (redis#1688) * Removed callbacks. Co-authored-by: Simon Prickett <simon@redislabs.com> Closes #1688. * Issue # 1697 FIX - creates an example script that shows how to use the SSCAN iterator (#1699) * #1697 fix for set scan example * adds the js file * adds comment * Minor layout and comment adjustment. Co-authored-by: srawat2 <shashank19aug> Co-authored-by: Simon Prickett <simon@redislabs.com> Closes #1697. * fix #1706 - HSET return type should be number * use dockers for tests, fix some bugs * increase dockers timeout to 30s * release drafter (#1683) * release drafter * fixing contributors * use dockers for tests, use npm workspaces, add rejson & redisearch modules, fix some bugs * fix #1712 - fix LINDEX return type * uncomment TIME tests * use codecov * fix tests.yml * uncomment "should handle live resharding" test * fix #1714 - update README(s) * add package-lock.json * update CONTRIBUTING.md * update examples * uncomment some tests * fix test-utils * move "all-in-one" to root folder * fix tests workflow * fix bug in cluster slots, enhance live resharding test * fix live resharding test * fix #1707 - handle number arguments in legacy mode * Add rejectedUnauthorized and other TLS options (#1708) * Update socket.ts * fix #1716 - decode username and password from url * fix some Z (sorted list) commands, increase commands test coverage * remove empty lines * fix 'Scenario' typo (#1720) * update readmes, add createCluster to the `redis` package * add .release-it.json files, update some md files * run tests on pull requests too * Support esModuleInterop set to false. (#1717) * Support esModuleInterop set to false. When testing the upcoming 4.x release, we got a bunch of typescript errors emitted from this project. We quickly realized this is because the library uses the esModuleInterop flag. This makes some imports _slightly_ easier to write, but it comes at a cost: it forces any application or library using this library to *also* have esModuleInterop on. The `esModuleInterop` flag is a bit of a holdover from an earlier time, and I would not recommend using it in libraries. The main issue is that if it's set to true, you are forcing any users of the library to also have `esModuleInterop`, where if you keep have it set to `false` (the default), you leave the decision to the user. This change should have no rammifications to users with `esModuleInterop` on, but it will enable support for those that have it off. This is especially good for library authors such as myself, because I would also like to keep this flag off to not force *my* users into this feature. * All tests now pass! * Move @types/redis-parser into client sub-package and removed a comma * npm update, remove html from readme * add tests and licence badges * update changelog.md * update .npmignore and .release-it.json * update .release-it.json * Release client@1.0.0-rc.0 * revertd32f1edf8a
* fix .npmignore * replace @redis with @node-redis * Release client@1.0.0-rc.0 * update json & search version * Release json@1.0.0-rc.0 * Release search@1.0.0-rc.0 * update dependencies * Release redis@4.0.0-rc.4 * fix #1724 - fix LINDEX signature * add positive test for LINDEX * fix #1718 - add support for buffers in pubsub * Fixed a few typos. * fix ARRPOP * fix #1726 * enhance cluster reshard handling * Adds RediSearch demo. * Adds intro sentence. * Made top level comment more descriptive. * Adds RedisJSON example. * Renamed JSON search example. * Some refactoring. * Fixed search example for JSON. * Minor wording updates. * Added missing pet name. * Adds JSON package overview. * Fixed typo. * Search package README initial version. * remove echo from docker entrypoint.sh * npm update * update docs * fix merge * fix merge * Release client@1.0.0 * npm update * Release search@1.0.0 * update sub modules * Release redis@4.0.0 Co-authored-by: Richard Samuelsson <noobtoothfairy@gmail.com> Co-authored-by: mustard <mhqnwt@gmail.com> Co-authored-by: Simon Prickett <simon@redislabs.com> Co-authored-by: Simon Prickett <simon@crudworks.org> Co-authored-by: Suze Shardlow <SuzeShardlow@users.noreply.github.com> Co-authored-by: Joshua T <buildingsomethingfun@gmail.com> Co-authored-by: Aditya Rastogi <adit.rastogi2014@gmail.com> Co-authored-by: Rohan Kumar <rohan.kr20@gmail.com> Co-authored-by: Kalki <shashank.kviit@gmail.com> Co-authored-by: Chayim <chayim@users.noreply.github.com> Co-authored-by: Da-Jin Chu <dajinchu@gmail.com> Co-authored-by: Henrique Corrêa <75134774+HeCorr@users.noreply.github.com> Co-authored-by: Evert Pot <me@evertpot.com>
This commit is contained in:
298
.github/README.md
vendored
298
.github/README.md
vendored
@@ -1,298 +0,0 @@
|
||||
# Node-Redis
|
||||
|
||||
[](https://codecov.io/gh/redis/node-redis)
|
||||
[](https://codecov.io/gh/redis/node-redis)
|
||||
[](https://codecov.io/gh/redis/node-redis)
|
||||
[](https://discord.gg/XMMVgxUm)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install redis@next
|
||||
```
|
||||
|
||||
> :warning: The new interface is clean and cool, but if you have an existing code base, you'll want to read the [migration guide](../docs/v3-to-v4.md).
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```typescript
|
||||
import { createClient } from 'redis';
|
||||
|
||||
(async () => {
|
||||
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');
|
||||
})();
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
### 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']
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
Subscribing to a channel requires a dedicated stand-alone connection. You can easily get one by `.duplicate()`ing an existing Redis connection.
|
||||
|
||||
```typescript
|
||||
const subscriber = client.duplicate();
|
||||
|
||||
await subscriber.connect();
|
||||
```
|
||||
|
||||
Once you have one, simply subscribe and unsubscribe as needed:
|
||||
|
||||
```typescript
|
||||
await subscriber.subscribe('channel', (message) => {
|
||||
console.log(message); // 'message'
|
||||
});
|
||||
|
||||
await subscriber.pSubscribe('channe*', (message, channel) => {
|
||||
console.log(message, channel); // 'message', 'channel'
|
||||
});
|
||||
|
||||
await subscriber.unsubscribe('channel');
|
||||
|
||||
await subscriber.pUnsubscribe('channe*');
|
||||
```
|
||||
|
||||
Publish a message on a channel:
|
||||
|
||||
```typescript
|
||||
await publisher.publish('channel', 'message');
|
||||
```
|
||||
|
||||
### 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, member } 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
|
||||
});
|
||||
```
|
||||
|
||||
### Lua Scripts
|
||||
|
||||
Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server:
|
||||
|
||||
```typescript
|
||||
import { createClient, defineScript } from 'redis';
|
||||
|
||||
(async () => {
|
||||
const client = createClient({
|
||||
scripts: {
|
||||
add: defineScript({
|
||||
NUMBER_OF_KEYS: 1,
|
||||
SCRIPT:
|
||||
'local val = redis.pcall("GET", KEYS[1]);' +
|
||||
'return val + ARGV[1];',
|
||||
transformArguments(key: string, toAdd: number): Array<string> {
|
||||
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.
|
||||
|
||||
## Supported Redis versions
|
||||
|
||||
Node Redis is supported with the following versions of Redis:
|
||||
|
||||
| Version | Supported |
|
||||
|---------|--------------------|
|
||||
| 6.2.z | :heavy_check_mark: |
|
||||
| 6.0.z | :heavy_check_mark: |
|
||||
| 5.y.z | :heavy_check_mark: |
|
||||
| < 5.0 | :x: |
|
||||
|
||||
> Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support.
|
||||
|
||||
## Packages
|
||||
|
||||
| Name | Description |
|
||||
|-------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [redis](../) | [](https://www.npmjs.com/package/redis/v/next) [](https://www.npmjs.com/package/redis/v/next) |
|
||||
| [@node-redis/client](../packages/client) | [](https://www.npmjs.com/package/@node-redis/client/v/next) [](https://www.npmjs.com/package/@node-redis/client/v/next) |
|
||||
| [@node-redis/json](../packages/json) | [](https://www.npmjs.com/package/@node-redis/json/v/next) [](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands |
|
||||
| [@node-redis/search](../packages/search) | [](https://www.npmjs.com/package/@node-redis/search/v/next) [](https://www.npmjs.com/package/@node-redis/search/v/next) [Redis Search](https://oss.redis.com/redisearch/) commands |
|
||||
|
||||
## 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!
|
||||
|
||||
[](https://github.com/redis/node-redis/graphs/contributors)
|
||||
|
||||
## License
|
||||
|
||||
This repository is licensed under the "MIT" license. See [LICENSE](LICENSE).
|
314
README.md
314
README.md
@@ -1,2 +1,312 @@
|
||||
# redis
|
||||
The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo.
|
||||
# Node-Redis
|
||||
|
||||
[](https://codecov.io/gh/redis/node-redis)
|
||||
[](https://codecov.io/gh/redis/node-redis)
|
||||
[](https://codecov.io/gh/redis/node-redis)
|
||||
[](https://discord.gg/XMMVgxUm)
|
||||
|
||||
node-redis is a modern, high performance [Redis](https://redis.io) client for Node.js with built-in support for Redis 6.2 commands and modules including [RediSearch](https://redisearch.io) and [RedisJSON](https://redisjson.io).
|
||||
|
||||
## Installation
|
||||
|
||||
```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).
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```typescript
|
||||
import { createClient } from 'redis';
|
||||
|
||||
(async () => {
|
||||
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');
|
||||
})();
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
### 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']
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
Subscribing to a channel requires a dedicated stand-alone connection. You can easily get one by `.duplicate()`ing an existing Redis connection.
|
||||
|
||||
```typescript
|
||||
const subscriber = client.duplicate();
|
||||
|
||||
await subscriber.connect();
|
||||
```
|
||||
|
||||
Once you have one, simply subscribe and unsubscribe as needed:
|
||||
|
||||
```typescript
|
||||
await subscriber.subscribe('channel', (message) => {
|
||||
console.log(message); // 'message'
|
||||
});
|
||||
|
||||
await subscriber.pSubscribe('channe*', (message, channel) => {
|
||||
console.log(message, channel); // 'message', 'channel'
|
||||
});
|
||||
|
||||
await subscriber.unsubscribe('channel');
|
||||
|
||||
await subscriber.pUnsubscribe('channe*');
|
||||
```
|
||||
|
||||
Publish a message on a channel:
|
||||
|
||||
```typescript
|
||||
await publisher.publish('channel', 'message');
|
||||
```
|
||||
|
||||
There is support for buffers as well:
|
||||
|
||||
```typescript
|
||||
await subscriber.subscribe('channel', (message) => {
|
||||
console.log(message); // <Buffer 6d 65 73 73 61 67 65>
|
||||
}, true);
|
||||
|
||||
await subscriber.pSubscribe('channe*', (message, channel) => {
|
||||
console.log(message, channel); // <Buffer 6d 65 73 73 61 67 65>, <Buffer 63 68 61 6e 6e 65 6c>
|
||||
}, true);
|
||||
```
|
||||
|
||||
### 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, member } 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
|
||||
});
|
||||
```
|
||||
|
||||
### Lua Scripts
|
||||
|
||||
Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server:
|
||||
|
||||
```typescript
|
||||
import { createClient, defineScript } from 'redis';
|
||||
|
||||
(async () => {
|
||||
const client = createClient({
|
||||
scripts: {
|
||||
add: defineScript({
|
||||
NUMBER_OF_KEYS: 1,
|
||||
SCRIPT:
|
||||
'local val = redis.pcall("GET", KEYS[1]);' +
|
||||
'return val + ARGV[1];',
|
||||
transformArguments(key: string, toAdd: number): Array<string> {
|
||||
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.
|
||||
|
||||
## Supported Redis versions
|
||||
|
||||
Node Redis is supported with the following versions of Redis:
|
||||
|
||||
| Version | Supported |
|
||||
|---------|--------------------|
|
||||
| 6.2.z | :heavy_check_mark: |
|
||||
| 6.0.z | :heavy_check_mark: |
|
||||
| 5.y.z | :heavy_check_mark: |
|
||||
| < 5.0 | :x: |
|
||||
|
||||
> Node Redis should work with older versions of Redis, but it is not fully tested and we cannot offer support.
|
||||
|
||||
## Packages
|
||||
|
||||
| Name | Description |
|
||||
|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [redis](./) | [](https://www.npmjs.com/package/redis/v/next) [](https://www.npmjs.com/package/redis/v/next) |
|
||||
| [@node-redis/client](./packages/client) | [](https://www.npmjs.com/package/@node-redis/client/v/next) [](https://www.npmjs.com/package/@node-redis/client/v/next) |
|
||||
| [@node-redis/json](./packages/json) | [](https://www.npmjs.com/package/@node-redis/json/v/next) [](https://www.npmjs.com/package/@node-redis/json/v/next) [Redis JSON](https://oss.redis.com/redisjson/) commands |
|
||||
| [@node-redis/search](./packages/search) | [](https://www.npmjs.com/package/@node-redis/search/v/next) [](https://www.npmjs.com/package/@node-redis/search/v/next) [Redis Search](https://oss.redis.com/redisearch/) commands |
|
||||
|
||||
## 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!
|
||||
|
||||
[](https://github.com/redis/node-redis/graphs/contributors)
|
||||
|
||||
## License
|
||||
|
||||
This repository is licensed under the "MIT" license. See [LICENSE](LICENSE).
|
||||
|
@@ -15,11 +15,11 @@
|
||||
| username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) |
|
||||
| password | | ACL password or the old "--requirepass" password |
|
||||
| database | | Database number to connect to (see [`SELECT`](https://redis.io/commands/select) command) |
|
||||
| modules | | Object defining which [Redis Modules](../.github/README.md#packages) to include |
|
||||
| modules | | Object defining which [Redis Modules](../README.md#packages) to include |
|
||||
| scripts | | Object defining Lua Scripts to use with this client (see [Lua Scripts](../README.md#lua-scripts)) |
|
||||
| commandsQueueMaxLength | | Maximum length of the client's internal command queue |
|
||||
| readonly | `false` | Connect in [`READONLY`](https://redis.io/commands/readonly) mode |
|
||||
| legacyMode | `false` | Maintain some backwards compatibility (see the [Migration Guide](v3-to-v4.md)) |
|
||||
| legacyMode | `false` | Maintain some backwards compatibility (see the [Migration Guide](./v3-to-v4.md)) |
|
||||
| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) |
|
||||
|
||||
## Reconnect Strategy
|
||||
|
@@ -38,7 +38,7 @@ import { createCluster } from 'redis';
|
||||
| defaults | | The default configuration values for every client in the cluster. Use this for example when specifying an ACL user to connect with |
|
||||
| useReplicas | `false` | When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes |
|
||||
| maxCommandRedirections | `16` | The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors |
|
||||
| modules | | Object defining which [Redis Modules](../../README.md#modules) to include |
|
||||
| modules | | Object defining which [Redis Modules](../README.md#modules) to include |
|
||||
| scripts | | Object defining Lua Scripts to use with this client (see [Lua Scripts](../README.md#lua-scripts)) |
|
||||
|
||||
## Command Routing
|
||||
|
@@ -4,7 +4,7 @@ Version 4 of Node Redis is a major refactor. While we have tried to maintain bac
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
See the [Change Log](../CHANGELOG.md).
|
||||
See the [Change Log](../packages/client/CHANGELOG.md).
|
||||
|
||||
## Promises
|
||||
|
||||
|
@@ -2,14 +2,16 @@
|
||||
|
||||
This folder contains example scripts showing how to use Node Redis in different scenarios.
|
||||
|
||||
| File Name | Description |
|
||||
|-----------------------------|------------------------------------------------------------------------------------|
|
||||
| `blocking-list-pop.js` | Block until an element is pushed to a list |
|
||||
| `command-with-modifiers.js` | Define a script that allows to run a command with several modifiers |
|
||||
| `connect-as-acl-user.js` | Connect to Redis 6 using an ACL user |
|
||||
| `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys |
|
||||
| `search+json.js` | Use [Redis Search](https://redisearch.io/) and [Redis JSON](https://redisjson.io/) |
|
||||
| `set-scan.js` | An example script that shows how to use the SSCAN iterator functionality |
|
||||
| File Name | Description |
|
||||
|-----------------------------|----------------------------------------------------------------------------------------------------------------|
|
||||
| `blocking-list-pop.js` | Block until an element is pushed to a list |
|
||||
| `command-with-modifiers.js` | Define a script that allows to run a command with several modifiers |
|
||||
| `connect-as-acl-user.js` | Connect to Redis 6 using an ACL user |
|
||||
| `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys |
|
||||
| `managing-json.js` | Store, retrieve and manipulate JSON data atomically with [RedisJSON](https://redisjson.io/) |
|
||||
| `search-hashes.js` | Uses [RediSearch](https://redisearch.io) to index and search data in hashes |
|
||||
| `search-json.js` | Uses [RediSearch](https://redisearch.io/) and [RedisJSON](https://redisjson.io/) to index and search JSON data |
|
||||
| `set-scan.js` | An example script that shows how to use the SSCAN iterator functionality |
|
||||
|
||||
## Contributing
|
||||
|
||||
|
81
examples/managing-json.js
Normal file
81
examples/managing-json.js
Normal file
@@ -0,0 +1,81 @@
|
||||
// Store, retrieve and manipulate JSON data atomically with RedisJSON.
|
||||
|
||||
import { createClient } from 'redis';
|
||||
|
||||
async function managingJSON() {
|
||||
const client = createClient();
|
||||
|
||||
await client.connect();
|
||||
await client.del('noderedis:jsondata');
|
||||
|
||||
// Store a JSON object...
|
||||
await client.json.set('noderedis:jsondata', '$', {
|
||||
name: 'Roberta McDonald',
|
||||
pets: [
|
||||
{
|
||||
name: 'Fluffy',
|
||||
species: 'dog',
|
||||
age: 5,
|
||||
isMammal: true
|
||||
},
|
||||
{
|
||||
name: 'Rex',
|
||||
species: 'dog',
|
||||
age: 3,
|
||||
isMammal: true
|
||||
},
|
||||
{
|
||||
name: 'Goldie',
|
||||
species: 'fish',
|
||||
age: 2,
|
||||
isMammal: false
|
||||
}
|
||||
],
|
||||
address: {
|
||||
number: 99,
|
||||
street: 'Main Street',
|
||||
city: 'Springfield',
|
||||
state: 'OH',
|
||||
country: 'USA'
|
||||
}
|
||||
});
|
||||
|
||||
// Retrieve the name and age of the second pet in the pets array.
|
||||
let results = await client.json.get('noderedis:jsondata', {
|
||||
path: [
|
||||
'.pets[1].name',
|
||||
'.pets[1].age'
|
||||
]
|
||||
});
|
||||
|
||||
// { '.pets[1].name': 'Rex', '.pets[1].age': 3 }
|
||||
console.log(results);
|
||||
|
||||
// Goldie had a birthday, increment the age...
|
||||
await client.json.numIncrBy('noderedis:jsondata', '.pets[2].age', 1);
|
||||
results = await client.json.get('noderedis:jsondata', {
|
||||
path: '.pets[2].age'
|
||||
});
|
||||
|
||||
// Goldie is 3 years old now.
|
||||
console.log(`Goldie is ${JSON.stringify(results)} years old now.`);
|
||||
|
||||
// Add a new pet...
|
||||
await client.json.arrAppend('noderedis:jsondata', '.pets', {
|
||||
name: 'Robin',
|
||||
species: 'bird',
|
||||
isMammal: false,
|
||||
age: 1
|
||||
});
|
||||
|
||||
// How many pets do we have now?
|
||||
const numPets = await client.json.arrLen('noderedis:jsondata', '.pets');
|
||||
|
||||
// We now have 4 pets.
|
||||
console.log(`We now have ${numPets} pets.`);
|
||||
|
||||
await client.quit();
|
||||
}
|
||||
|
||||
managingJSON();
|
||||
|
@@ -1,74 +0,0 @@
|
||||
// Use Redis Search and Redis JSON
|
||||
|
||||
import { createClient, SchemaFieldTypes, AggregateGroupByReducers, AggregateSteps } from 'redis';
|
||||
|
||||
async function searchPlusJson() {
|
||||
const client = createClient();
|
||||
|
||||
await client.connect();
|
||||
|
||||
// Create an index
|
||||
await client.ft.create('users', {
|
||||
'$.name': {
|
||||
type: SchemaFieldTypes.TEXT,
|
||||
SORTABLE: 'UNF'
|
||||
},
|
||||
'$.age': SchemaFieldTypes.NUMERIC,
|
||||
'$.coins': SchemaFieldTypes.NUMERIC
|
||||
}, {
|
||||
ON: 'JSON'
|
||||
});
|
||||
|
||||
// Add some users
|
||||
await Promise.all([
|
||||
client.json.set('users:1', '$', {
|
||||
name: 'Alice',
|
||||
age: 32,
|
||||
coins: 100
|
||||
}),
|
||||
client.json.set('users:2', '$', {
|
||||
name: 'Bob',
|
||||
age: 23,
|
||||
coins: 15
|
||||
})
|
||||
]);
|
||||
|
||||
// Search all users under 30
|
||||
// TODO: why "$.age:[-inf, 30]" does not work?
|
||||
console.log(
|
||||
await client.ft.search('users', '*')
|
||||
);
|
||||
// {
|
||||
// total: 1,
|
||||
// documents: [...]
|
||||
// }
|
||||
|
||||
// Some aggrigrations
|
||||
console.log(
|
||||
await client.ft.aggregate('users', '*', {
|
||||
STEPS: [{
|
||||
type: AggregateSteps.GROUPBY,
|
||||
REDUCE: [{
|
||||
type: AggregateGroupByReducers.AVG,
|
||||
property: '$.age',
|
||||
AS: 'avarageAge'
|
||||
}, {
|
||||
type: AggregateGroupByReducers.SUM,
|
||||
property: '$.coins',
|
||||
AS: 'totalCoins'
|
||||
}]
|
||||
}]
|
||||
})
|
||||
);
|
||||
// {
|
||||
// total: 2,
|
||||
// results: [{
|
||||
// avarageAvg: '27.5',
|
||||
// totalCoins: '115'
|
||||
// }]
|
||||
// }
|
||||
|
||||
await client.quit();
|
||||
}
|
||||
|
||||
searchPlusJson();
|
82
examples/search-hashes.js
Normal file
82
examples/search-hashes.js
Normal file
@@ -0,0 +1,82 @@
|
||||
// This example demonstrates how to use RediSearch to index and query data
|
||||
// stored in Redis hashes.
|
||||
|
||||
import { createClient, SchemaFieldTypes } from 'redis';
|
||||
|
||||
async function searchHashes() {
|
||||
const client = createClient();
|
||||
|
||||
await client.connect();
|
||||
|
||||
// Create an index...
|
||||
try {
|
||||
// Documentation: https://oss.redis.com/redisearch/Commands/#ftcreate
|
||||
await client.ft.create('idx:animals', {
|
||||
name: {
|
||||
type: SchemaFieldTypes.TEXT,
|
||||
sortable: true
|
||||
},
|
||||
species: SchemaFieldTypes.TAG,
|
||||
age: SchemaFieldTypes.NUMERIC
|
||||
}, {
|
||||
ON: 'HASH',
|
||||
PREFIX: 'noderedis:animals'
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.message === 'Index already exists') {
|
||||
console.log('Index exists already, skipped creation.');
|
||||
} else {
|
||||
// Something went wrong, perhaps RediSearch isn't installed...
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Add some sample data...
|
||||
await Promise.all([
|
||||
client.hSet('noderedis:animals:1', {name: 'Fluffy', species: 'cat', age: 3}),
|
||||
client.hSet('noderedis:animals:2', {name: 'Ginger', species: 'cat', age: 4}),
|
||||
client.hSet('noderedis:animals:3', {name: 'Rover', species: 'dog', age: 9}),
|
||||
client.hSet('noderedis:animals:4', {name: 'Fido', species: 'dog', age: 7})
|
||||
]);
|
||||
|
||||
// Perform a search query, find all the dogs...
|
||||
// Documentation: https://oss.redis.com/redisearch/Commands/#ftsearch
|
||||
// Query synatax: https://oss.redis.com/redisearch/Query_Syntax/
|
||||
const results = await client.ft.search('idx:animals', '@species:{dog}');
|
||||
|
||||
// results:
|
||||
// {
|
||||
// total: 2,
|
||||
// documents: [
|
||||
// {
|
||||
// id: 'noderedis:animals:4',
|
||||
// value: {
|
||||
// name: 'Fido',
|
||||
// species: 'dog',
|
||||
// age: '7'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// id: 'noderedis:animals:3',
|
||||
// value: {
|
||||
// name: 'Rover',
|
||||
// species: 'dog',
|
||||
// age: '9'
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
|
||||
console.log(`Results found: ${results.total}.`);
|
||||
|
||||
for (const doc of results.documents) {
|
||||
// noderedis:animals:4: Fido
|
||||
// noderedis:animals:3: Rover
|
||||
console.log(`${doc.id}: ${doc.value.name}`);
|
||||
}
|
||||
|
||||
await client.quit();
|
||||
}
|
||||
|
||||
searchHashes();
|
93
examples/search-json.js
Normal file
93
examples/search-json.js
Normal file
@@ -0,0 +1,93 @@
|
||||
// This example demonstrates how to use RediSearch and RedisJSON together.
|
||||
|
||||
import { createClient, SchemaFieldTypes, AggregateGroupByReducers, AggregateSteps } from 'redis';
|
||||
|
||||
async function searchJSON() {
|
||||
const client = createClient();
|
||||
|
||||
await client.connect();
|
||||
|
||||
// Create an index.
|
||||
try {
|
||||
await client.ft.create('idx:users', {
|
||||
'$.name': {
|
||||
type: SchemaFieldTypes.TEXT,
|
||||
SORTABLE: 'UNF'
|
||||
},
|
||||
'$.age': {
|
||||
type: SchemaFieldTypes.NUMERIC,
|
||||
AS: 'age'
|
||||
},
|
||||
'$.coins': {
|
||||
type: SchemaFieldTypes.NUMERIC,
|
||||
AS: 'coins'
|
||||
}
|
||||
}, {
|
||||
ON: 'JSON',
|
||||
PREFIX: 'noderedis:users'
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.message === 'Index already exists') {
|
||||
console.log('Index exists already, skipped creation.');
|
||||
} else {
|
||||
// Something went wrong, perhaps RediSearch isn't installed...
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Add some users.
|
||||
await Promise.all([
|
||||
client.json.set('noderedis:users:1', '$', {
|
||||
name: 'Alice',
|
||||
age: 32,
|
||||
coins: 100
|
||||
}),
|
||||
client.json.set('noderedis:users:2', '$', {
|
||||
name: 'Bob',
|
||||
age: 23,
|
||||
coins: 15
|
||||
})
|
||||
]);
|
||||
|
||||
// Search all users under 30
|
||||
console.log('Users under 30 years old:');
|
||||
console.log(
|
||||
// https://oss.redis.com/redisearch/Commands/#ftsearch
|
||||
await client.ft.search('idx:users', '@age:[0 30]')
|
||||
);
|
||||
// {
|
||||
// total: 1,
|
||||
// documents: [ { id: 'noderedis:users:2', value: [Object] } ]
|
||||
// }
|
||||
|
||||
// Some aggregrations, what's the average age and total number of coins...
|
||||
// https://oss.redis.com/redisearch/Commands/#ftaggregate
|
||||
console.log(
|
||||
await client.ft.aggregate('idx:users', '*', {
|
||||
STEPS: [{
|
||||
type: AggregateSteps.GROUPBY,
|
||||
REDUCE: [{
|
||||
type: AggregateGroupByReducers.AVG,
|
||||
property: 'age',
|
||||
AS: 'averageAge'
|
||||
}, {
|
||||
type: AggregateGroupByReducers.SUM,
|
||||
property: 'coins',
|
||||
AS: 'totalCoins'
|
||||
}]
|
||||
}]
|
||||
})
|
||||
);
|
||||
// {
|
||||
// total: 2,
|
||||
// results: [{
|
||||
// averageAge: '27.5',
|
||||
// totalCoins: '115'
|
||||
// }]
|
||||
// }
|
||||
|
||||
await client.quit();
|
||||
}
|
||||
|
||||
searchJSON();
|
519
package-lock.json
generated
519
package-lock.json
generated
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"name": "redis",
|
||||
"version": "4.0.0-rc.4",
|
||||
"version": "4.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "redis",
|
||||
"version": "4.0.0-rc.4",
|
||||
"version": "4.0.0",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"./packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-redis/client": "^1.0.0-rc",
|
||||
"@node-redis/json": "^1.0.0-rc",
|
||||
"@node-redis/search": "^1.0.0-rc"
|
||||
"@node-redis/client": "^1.0.0",
|
||||
"@node-redis/json": "^1.0.0",
|
||||
"@node-redis/search": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node12": "^1.0.9",
|
||||
"release-it": "^14.11.7",
|
||||
"typescript": "^4.4.4"
|
||||
"release-it": "^14.11.8",
|
||||
"typescript": "^4.5.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@@ -35,9 +35,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.0.tgz",
|
||||
"integrity": "sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==",
|
||||
"version": "7.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz",
|
||||
"integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -364,9 +364,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.3.tgz",
|
||||
"integrity": "sha512-dcNwU1O4sx57ClvLBVFbEgx0UZWfd0JQX5X6fxFRCLHelFBGXFfSz6Y0FAq2PEwUqlqLkdVjVr4VASEOuUnLJw==",
|
||||
"version": "7.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz",
|
||||
"integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -669,6 +669,10 @@
|
||||
"resolved": "packages/test-utils",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@node-redis/time-series": {
|
||||
"resolved": "packages/time-series",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -816,15 +820,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest": {
|
||||
"version": "18.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz",
|
||||
"integrity": "sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==",
|
||||
"version": "18.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz",
|
||||
"integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@octokit/core": "^3.5.1",
|
||||
"@octokit/plugin-paginate-rest": "^2.16.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.16.8",
|
||||
"@octokit/plugin-request-log": "^1.0.4",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.9.0"
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
@@ -959,9 +963,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "16.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz",
|
||||
"integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==",
|
||||
"version": "16.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz",
|
||||
"integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
@@ -1011,9 +1015,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/yargs": {
|
||||
"version": "17.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.5.tgz",
|
||||
"integrity": "sha512-4HNq144yhaVjJs+ON6A07NEoi9Hh0Rhl/jI9Nt/l/YRjt+T6St/QK3meFARWZ8IgkzoD1LC0PdTdJenlQQi2WQ==",
|
||||
"version": "17.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.7.tgz",
|
||||
"integrity": "sha512-OvLKmpKdea1aWtqHv9bxVVcMoT6syAeK+198dfETIFkAevYRGwqh4H+KFxfjUETZuUuE5sQCAFwdOdoHUdo8eg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
@@ -1189,9 +1193,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz",
|
||||
"integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==",
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz",
|
||||
"integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@@ -1630,9 +1634,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001280",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001280.tgz",
|
||||
"integrity": "sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA==",
|
||||
"version": "1.0.30001282",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001282.tgz",
|
||||
"integrity": "sha512-YhF/hG6nqBEllymSIjLtR2iWDDnChvhnVJqp+vloyt2tEHFG1yBR+ac2B/rOw0qOK0m0lEXU2dv4E/sMk5P9Kg==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -1683,9 +1687,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ci-info": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz",
|
||||
"integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz",
|
||||
"integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/clean-stack": {
|
||||
@@ -2085,9 +2089,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.3.897",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.897.tgz",
|
||||
"integrity": "sha512-nRNZhAZ7hVCe75jrCUG7xLOqHMwloJMj6GEXEzY4OMahRGgwerAo+ls/qbqUwFH+E20eaSncKkQ4W8KP5SOiAg==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.0.tgz",
|
||||
"integrity": "sha512-+oXCt6SaIu8EmFTPx8wNGSB0tHQ5biDscnlf6Uxuz17e9CjzMRtGk9B8705aMPnj0iWr3iC74WuIkngCsLElmA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
@@ -2160,9 +2164,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz",
|
||||
"integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==",
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.3.0.tgz",
|
||||
"integrity": "sha512-aIay56Ph6RxOTC7xyr59Kt3ewX185SaGnAr8eWukoPLeriCrvGjvAubxuvaXOfsxhtwV5g0uBOsyhAom4qJdww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint/eslintrc": "^1.0.4",
|
||||
@@ -2174,10 +2178,10 @@
|
||||
"doctrine": "^3.0.0",
|
||||
"enquirer": "^2.3.5",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"eslint-scope": "^6.0.0",
|
||||
"eslint-scope": "^7.1.0",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"eslint-visitor-keys": "^3.0.0",
|
||||
"espree": "^9.0.0",
|
||||
"eslint-visitor-keys": "^3.1.0",
|
||||
"espree": "^9.1.0",
|
||||
"esquery": "^1.4.0",
|
||||
"esutils": "^2.0.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
@@ -2276,9 +2280,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/eslint-scope": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz",
|
||||
"integrity": "sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz",
|
||||
"integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esrecurse": "^4.3.0",
|
||||
@@ -2331,14 +2335,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz",
|
||||
"integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==",
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.1.0.tgz",
|
||||
"integrity": "sha512-ZgYLvCS1wxOczBYGcQT9DDWgicXwJ4dbocr9uYN+/eresBAUuBu+O4WzB21ufQ/JqQT8gyp7hJ3z8SHii32mTQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.5.0",
|
||||
"acorn": "^8.6.0",
|
||||
"acorn-jsx": "^5.3.1",
|
||||
"eslint-visitor-keys": "^3.0.0"
|
||||
"eslint-visitor-keys": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -2860,9 +2864,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/got": {
|
||||
"version": "11.8.2",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz",
|
||||
"integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==",
|
||||
"version": "11.8.3",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz",
|
||||
"integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@sindresorhus/is": "^4.0.0",
|
||||
@@ -2870,7 +2874,7 @@
|
||||
"@types/cacheable-request": "^6.0.1",
|
||||
"@types/responselike": "^1.0.0",
|
||||
"cacheable-lookup": "^5.0.3",
|
||||
"cacheable-request": "^7.0.1",
|
||||
"cacheable-request": "^7.0.2",
|
||||
"decompress-response": "^6.0.0",
|
||||
"http2-wrapper": "^1.0.0-beta.5.2",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
@@ -3182,9 +3186,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/inquirer": {
|
||||
"version": "8.1.5",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz",
|
||||
"integrity": "sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg==",
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz",
|
||||
"integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-escapes": "^4.2.1",
|
||||
@@ -3234,12 +3238,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/is-ci": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz",
|
||||
"integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
|
||||
"integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ci-info": "^3.1.1"
|
||||
"ci-info": "^3.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"is-ci": "bin.js"
|
||||
@@ -3701,9 +3705,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
||||
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
@@ -3871,21 +3875,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.49.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
|
||||
"integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
|
||||
"version": "1.51.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
|
||||
"integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.32",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
|
||||
"integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
|
||||
"version": "2.1.34",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
|
||||
"integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mime-db": "1.49.0"
|
||||
"mime-db": "1.51.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -5227,13 +5231,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/release-it": {
|
||||
"version": "14.11.7",
|
||||
"resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.7.tgz",
|
||||
"integrity": "sha512-m4p9+x6AEQPczc96Jyg6dGFeovpJVgRCtA1lxeIgTmQVt9dutYPkkjZeJngZgUJ17/Lb1bx6ZzW2qsKmopKnbQ==",
|
||||
"version": "14.11.8",
|
||||
"resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.8.tgz",
|
||||
"integrity": "sha512-951DJ0kwjwU7CwGU3BCvRBgLxuJsOPRrZkqx0AsugJdSyPpUdwY9nlU0RAoSKqgh+VTerzecXLIIwgsGIpNxlA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@octokit/rest": "18.10.0",
|
||||
"@octokit/rest": "18.12.0",
|
||||
"async-retry": "1.3.3",
|
||||
"chalk": "4.1.2",
|
||||
"cosmiconfig": "7.0.1",
|
||||
@@ -5243,12 +5247,12 @@
|
||||
"form-data": "4.0.0",
|
||||
"git-url-parse": "11.6.0",
|
||||
"globby": "11.0.4",
|
||||
"got": "11.8.2",
|
||||
"got": "11.8.3",
|
||||
"import-cwd": "3.0.0",
|
||||
"inquirer": "8.1.5",
|
||||
"is-ci": "3.0.0",
|
||||
"inquirer": "8.2.0",
|
||||
"is-ci": "3.0.1",
|
||||
"lodash": "4.17.21",
|
||||
"mime-types": "2.1.32",
|
||||
"mime-types": "2.1.34",
|
||||
"new-github-release-url": "1.0.0",
|
||||
"open": "7.4.2",
|
||||
"ora": "5.4.1",
|
||||
@@ -5562,9 +5566,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz",
|
||||
"integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==",
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
|
||||
"integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/sinon": {
|
||||
@@ -5613,9 +5617,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.20",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz",
|
||||
"integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==",
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
@@ -5940,9 +5944,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc": {
|
||||
"version": "0.22.9",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.9.tgz",
|
||||
"integrity": "sha512-84PjudoXVcap6bwdZFbYIUWlgdz/iLV09ZHwrCzhtHWXaDQG6mlosJ8te6DSThuRkRvQjp46HO+qY/P7Gpm78g==",
|
||||
"version": "0.22.10",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.10.tgz",
|
||||
"integrity": "sha512-hQYZ4WtoMZ61wDC6w10kxA42+jclWngdmztNZsDvIz7BMJg7F2xnT+uYsUa7OluyKossdFj9E9Ye4QOZKTy8SA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.2.0",
|
||||
@@ -5958,7 +5962,7 @@
|
||||
"node": ">= 12.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x"
|
||||
"typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x"
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc-github-wiki-theme": {
|
||||
@@ -5972,9 +5976,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc-plugin-markdown": {
|
||||
"version": "3.11.6",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.6.tgz",
|
||||
"integrity": "sha512-CV1BuxL7HR/EE1ctnPXOWzf4/Exl0FzkwtFVYaKTVWTnD/dkFLgABOfWuOL4lPmzLUOsAL85pmq+/PB6cdRppw==",
|
||||
"version": "3.11.7",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.7.tgz",
|
||||
"integrity": "sha512-Wm3HP5gcBOGOOTeDA8GLgw+BY+GAI31RP9Lyog21BvTaSeWUcdXls5TG1MK+XDatS2/0dup9gFO+emoyoQJm9Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"handlebars": "^4.7.7"
|
||||
@@ -5984,9 +5988,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz",
|
||||
"integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@@ -6412,7 +6416,7 @@
|
||||
},
|
||||
"packages/client": {
|
||||
"name": "@node-redis/client",
|
||||
"version": "1.0.0-rc",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cluster-key-slot": "1.1.0",
|
||||
@@ -6423,22 +6427,22 @@
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@node-redis/test-utils": "*",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/redis-parser": "^3.0.0",
|
||||
"@types/sinon": "^10.0.6",
|
||||
"@types/yallist": "^4.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint": "^8.2.0",
|
||||
"eslint": "^8.3.0",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.7",
|
||||
"release-it": "^14.11.8",
|
||||
"sinon": "^12.0.1",
|
||||
"source-map-support": "^0.5.20",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typedoc": "^0.22.9",
|
||||
"typedoc": "^0.22.10",
|
||||
"typedoc-github-wiki-theme": "^0.6.0",
|
||||
"typedoc-plugin-markdown": "^3.11.6",
|
||||
"typescript": "^4.4.4"
|
||||
"typedoc-plugin-markdown": "^3.11.7",
|
||||
"typescript": "^4.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -6451,19 +6455,56 @@
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@node-redis/test-utils": "*",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/node": "^16.11.10",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.7",
|
||||
"source-map-support": "^0.5.20",
|
||||
"release-it": "^14.11.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.4.4"
|
||||
"typescript": "^4.5.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@node-redis/client": "^1.0.0-rc"
|
||||
"@node-redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"packages/search": {
|
||||
"name": "@node-redis/search",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@node-redis/test-utils": "*",
|
||||
"@types/node": "^16.11.10",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.5.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@node-redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"packages/test-utils": {
|
||||
"name": "@node-redis/test-utils",
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/yargs": "^17.0.7",
|
||||
"mocha": "^9.1.3",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.5.2",
|
||||
"yargs": "^17.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@node-redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"packages/time-series": {
|
||||
"name": "@node-redis/time-series",
|
||||
"version": "1.0.0-rc.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
@@ -6477,26 +6518,7 @@
|
||||
"typescript": "^4.4.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@node-redis/client": "^1.0.0-rc"
|
||||
}
|
||||
},
|
||||
"packages/test-utils": {
|
||||
"name": "@node-redis/test-utils",
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/yargs": "^17.0.5",
|
||||
"mocha": "^9.1.3",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.7",
|
||||
"source-map-support": "^0.5.20",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.4.4",
|
||||
"yargs": "^17.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@node-redis/client": "^1.0.0-rc"
|
||||
"@node-redis/client": "^1.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6511,9 +6533,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/compat-data": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.0.tgz",
|
||||
"integrity": "sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==",
|
||||
"version": "7.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz",
|
||||
"integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/core": {
|
||||
@@ -6767,9 +6789,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.3.tgz",
|
||||
"integrity": "sha512-dcNwU1O4sx57ClvLBVFbEgx0UZWfd0JQX5X6fxFRCLHelFBGXFfSz6Y0FAq2PEwUqlqLkdVjVr4VASEOuUnLJw==",
|
||||
"version": "7.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz",
|
||||
"integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/template": {
|
||||
@@ -6990,25 +7012,25 @@
|
||||
"requires": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@node-redis/test-utils": "*",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/redis-parser": "^3.0.0",
|
||||
"@types/sinon": "^10.0.6",
|
||||
"@types/yallist": "^4.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"cluster-key-slot": "1.1.0",
|
||||
"eslint": "^8.2.0",
|
||||
"eslint": "^8.3.0",
|
||||
"generic-pool": "3.8.2",
|
||||
"nyc": "^15.1.0",
|
||||
"redis-parser": "3.0.0",
|
||||
"release-it": "^14.11.7",
|
||||
"release-it": "^14.11.8",
|
||||
"sinon": "^12.0.1",
|
||||
"source-map-support": "^0.5.20",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typedoc": "^0.22.9",
|
||||
"typedoc": "^0.22.10",
|
||||
"typedoc-github-wiki-theme": "^0.6.0",
|
||||
"typedoc-plugin-markdown": "^3.11.6",
|
||||
"typescript": "^4.4.4",
|
||||
"typedoc-plugin-markdown": "^3.11.7",
|
||||
"typescript": "^4.5.2",
|
||||
"yallist": "4.0.0"
|
||||
}
|
||||
},
|
||||
@@ -7017,16 +7039,45 @@
|
||||
"requires": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@node-redis/test-utils": "*",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/node": "^16.11.10",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.7",
|
||||
"source-map-support": "^0.5.20",
|
||||
"release-it": "^14.11.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.4.4"
|
||||
"typescript": "^4.5.2"
|
||||
}
|
||||
},
|
||||
"@node-redis/search": {
|
||||
"version": "file:packages/search",
|
||||
"requires": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@node-redis/test-utils": "*",
|
||||
"@types/node": "^16.11.10",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.5.2"
|
||||
}
|
||||
},
|
||||
"@node-redis/test-utils": {
|
||||
"version": "file:packages/test-utils",
|
||||
"requires": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/yargs": "^17.0.7",
|
||||
"mocha": "^9.1.3",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.5.2",
|
||||
"yargs": "^17.2.1"
|
||||
}
|
||||
},
|
||||
"@node-redis/time-series": {
|
||||
"version": "file:packages/time-series",
|
||||
"requires": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@node-redis/test-utils": "*",
|
||||
@@ -7038,22 +7089,6 @@
|
||||
"typescript": "^4.4.4"
|
||||
}
|
||||
},
|
||||
"@node-redis/test-utils": {
|
||||
"version": "file:packages/test-utils",
|
||||
"requires": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/yargs": "^17.0.5",
|
||||
"mocha": "^9.1.3",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.7",
|
||||
"source-map-support": "^0.5.20",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.4.4",
|
||||
"yargs": "^17.2.1"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -7184,15 +7219,15 @@
|
||||
}
|
||||
},
|
||||
"@octokit/rest": {
|
||||
"version": "18.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.10.0.tgz",
|
||||
"integrity": "sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==",
|
||||
"version": "18.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz",
|
||||
"integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@octokit/core": "^3.5.1",
|
||||
"@octokit/plugin-paginate-rest": "^2.16.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.16.8",
|
||||
"@octokit/plugin-request-log": "^1.0.4",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.9.0"
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.12.0"
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
@@ -7318,9 +7353,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "16.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz",
|
||||
"integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==",
|
||||
"version": "16.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz",
|
||||
"integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/parse-json": {
|
||||
@@ -7370,9 +7405,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "17.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.5.tgz",
|
||||
"integrity": "sha512-4HNq144yhaVjJs+ON6A07NEoi9Hh0Rhl/jI9Nt/l/YRjt+T6St/QK3meFARWZ8IgkzoD1LC0PdTdJenlQQi2WQ==",
|
||||
"version": "17.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.7.tgz",
|
||||
"integrity": "sha512-OvLKmpKdea1aWtqHv9bxVVcMoT6syAeK+198dfETIFkAevYRGwqh4H+KFxfjUETZuUuE5sQCAFwdOdoHUdo8eg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
@@ -7474,9 +7509,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz",
|
||||
"integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==",
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz",
|
||||
"integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-jsx": {
|
||||
@@ -7791,9 +7826,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001280",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001280.tgz",
|
||||
"integrity": "sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA==",
|
||||
"version": "1.0.30001282",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001282.tgz",
|
||||
"integrity": "sha512-YhF/hG6nqBEllymSIjLtR2iWDDnChvhnVJqp+vloyt2tEHFG1yBR+ac2B/rOw0qOK0m0lEXU2dv4E/sMk5P9Kg==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
@@ -7829,9 +7864,9 @@
|
||||
}
|
||||
},
|
||||
"ci-info": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz",
|
||||
"integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz",
|
||||
"integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==",
|
||||
"dev": true
|
||||
},
|
||||
"clean-stack": {
|
||||
@@ -8137,9 +8172,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.897",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.897.tgz",
|
||||
"integrity": "sha512-nRNZhAZ7hVCe75jrCUG7xLOqHMwloJMj6GEXEzY4OMahRGgwerAo+ls/qbqUwFH+E20eaSncKkQ4W8KP5SOiAg==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.0.tgz",
|
||||
"integrity": "sha512-+oXCt6SaIu8EmFTPx8wNGSB0tHQ5biDscnlf6Uxuz17e9CjzMRtGk9B8705aMPnj0iWr3iC74WuIkngCsLElmA==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
@@ -8200,9 +8235,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"eslint": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz",
|
||||
"integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==",
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.3.0.tgz",
|
||||
"integrity": "sha512-aIay56Ph6RxOTC7xyr59Kt3ewX185SaGnAr8eWukoPLeriCrvGjvAubxuvaXOfsxhtwV5g0uBOsyhAom4qJdww==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint/eslintrc": "^1.0.4",
|
||||
@@ -8214,10 +8249,10 @@
|
||||
"doctrine": "^3.0.0",
|
||||
"enquirer": "^2.3.5",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"eslint-scope": "^6.0.0",
|
||||
"eslint-scope": "^7.1.0",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"eslint-visitor-keys": "^3.0.0",
|
||||
"espree": "^9.0.0",
|
||||
"eslint-visitor-keys": "^3.1.0",
|
||||
"espree": "^9.1.0",
|
||||
"esquery": "^1.4.0",
|
||||
"esutils": "^2.0.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
@@ -8252,9 +8287,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz",
|
||||
"integrity": "sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz",
|
||||
"integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esrecurse": "^4.3.0",
|
||||
@@ -8324,14 +8359,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"espree": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz",
|
||||
"integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==",
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.1.0.tgz",
|
||||
"integrity": "sha512-ZgYLvCS1wxOczBYGcQT9DDWgicXwJ4dbocr9uYN+/eresBAUuBu+O4WzB21ufQ/JqQT8gyp7hJ3z8SHii32mTQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "^8.5.0",
|
||||
"acorn": "^8.6.0",
|
||||
"acorn-jsx": "^5.3.1",
|
||||
"eslint-visitor-keys": "^3.0.0"
|
||||
"eslint-visitor-keys": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"esprima": {
|
||||
@@ -8705,9 +8740,9 @@
|
||||
}
|
||||
},
|
||||
"got": {
|
||||
"version": "11.8.2",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz",
|
||||
"integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==",
|
||||
"version": "11.8.3",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz",
|
||||
"integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sindresorhus/is": "^4.0.0",
|
||||
@@ -8715,7 +8750,7 @@
|
||||
"@types/cacheable-request": "^6.0.1",
|
||||
"@types/responselike": "^1.0.0",
|
||||
"cacheable-lookup": "^5.0.3",
|
||||
"cacheable-request": "^7.0.1",
|
||||
"cacheable-request": "^7.0.2",
|
||||
"decompress-response": "^6.0.0",
|
||||
"http2-wrapper": "^1.0.0-beta.5.2",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
@@ -8933,9 +8968,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "8.1.5",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz",
|
||||
"integrity": "sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg==",
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz",
|
||||
"integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-escapes": "^4.2.1",
|
||||
@@ -8976,12 +9011,12 @@
|
||||
}
|
||||
},
|
||||
"is-ci": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz",
|
||||
"integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
|
||||
"integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ci-info": "^3.1.1"
|
||||
"ci-info": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"is-core-module": {
|
||||
@@ -9325,9 +9360,9 @@
|
||||
}
|
||||
},
|
||||
"lines-and-columns": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
||||
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"dev": true
|
||||
},
|
||||
"locate-path": {
|
||||
@@ -9452,18 +9487,18 @@
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.49.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
|
||||
"integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
|
||||
"version": "1.51.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
|
||||
"integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
|
||||
"dev": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.32",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
|
||||
"integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
|
||||
"version": "2.1.34",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
|
||||
"integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mime-db": "1.49.0"
|
||||
"mime-db": "1.51.0"
|
||||
}
|
||||
},
|
||||
"mimic-fn": {
|
||||
@@ -10482,13 +10517,13 @@
|
||||
}
|
||||
},
|
||||
"release-it": {
|
||||
"version": "14.11.7",
|
||||
"resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.7.tgz",
|
||||
"integrity": "sha512-m4p9+x6AEQPczc96Jyg6dGFeovpJVgRCtA1lxeIgTmQVt9dutYPkkjZeJngZgUJ17/Lb1bx6ZzW2qsKmopKnbQ==",
|
||||
"version": "14.11.8",
|
||||
"resolved": "https://registry.npmjs.org/release-it/-/release-it-14.11.8.tgz",
|
||||
"integrity": "sha512-951DJ0kwjwU7CwGU3BCvRBgLxuJsOPRrZkqx0AsugJdSyPpUdwY9nlU0RAoSKqgh+VTerzecXLIIwgsGIpNxlA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@octokit/rest": "18.10.0",
|
||||
"@octokit/rest": "18.12.0",
|
||||
"async-retry": "1.3.3",
|
||||
"chalk": "4.1.2",
|
||||
"cosmiconfig": "7.0.1",
|
||||
@@ -10498,12 +10533,12 @@
|
||||
"form-data": "4.0.0",
|
||||
"git-url-parse": "11.6.0",
|
||||
"globby": "11.0.4",
|
||||
"got": "11.8.2",
|
||||
"got": "11.8.3",
|
||||
"import-cwd": "3.0.0",
|
||||
"inquirer": "8.1.5",
|
||||
"is-ci": "3.0.0",
|
||||
"inquirer": "8.2.0",
|
||||
"is-ci": "3.0.1",
|
||||
"lodash": "4.17.21",
|
||||
"mime-types": "2.1.32",
|
||||
"mime-types": "2.1.34",
|
||||
"new-github-release-url": "1.0.0",
|
||||
"open": "7.4.2",
|
||||
"ora": "5.4.1",
|
||||
@@ -10727,9 +10762,9 @@
|
||||
}
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz",
|
||||
"integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==",
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
|
||||
"integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==",
|
||||
"dev": true
|
||||
},
|
||||
"sinon": {
|
||||
@@ -10770,9 +10805,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.20",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz",
|
||||
"integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==",
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
@@ -11016,9 +11051,9 @@
|
||||
}
|
||||
},
|
||||
"typedoc": {
|
||||
"version": "0.22.9",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.9.tgz",
|
||||
"integrity": "sha512-84PjudoXVcap6bwdZFbYIUWlgdz/iLV09ZHwrCzhtHWXaDQG6mlosJ8te6DSThuRkRvQjp46HO+qY/P7Gpm78g==",
|
||||
"version": "0.22.10",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.10.tgz",
|
||||
"integrity": "sha512-hQYZ4WtoMZ61wDC6w10kxA42+jclWngdmztNZsDvIz7BMJg7F2xnT+uYsUa7OluyKossdFj9E9Ye4QOZKTy8SA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.2.0",
|
||||
@@ -11036,18 +11071,18 @@
|
||||
"requires": {}
|
||||
},
|
||||
"typedoc-plugin-markdown": {
|
||||
"version": "3.11.6",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.6.tgz",
|
||||
"integrity": "sha512-CV1BuxL7HR/EE1ctnPXOWzf4/Exl0FzkwtFVYaKTVWTnD/dkFLgABOfWuOL4lPmzLUOsAL85pmq+/PB6cdRppw==",
|
||||
"version": "3.11.7",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.11.7.tgz",
|
||||
"integrity": "sha512-Wm3HP5gcBOGOOTeDA8GLgw+BY+GAI31RP9Lyog21BvTaSeWUcdXls5TG1MK+XDatS2/0dup9gFO+emoyoQJm9Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"handlebars": "^4.7.7"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz",
|
||||
"integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
|
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "redis",
|
||||
"version": "4.0.0-rc.4",
|
||||
"version": "4.0.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -17,14 +17,14 @@
|
||||
"build-all": "npm run build:client && npm run build:test-utils && npm run build:modules && npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@node-redis/client": "^1.0.0-rc.0",
|
||||
"@node-redis/json": "^1.0.0-rc.0",
|
||||
"@node-redis/search": "^1.0.0-rc.0"
|
||||
"@node-redis/client": "^1.0.0",
|
||||
"@node-redis/json": "^1.0.0",
|
||||
"@node-redis/search": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node12": "^1.0.9",
|
||||
"release-it": "^14.11.7",
|
||||
"typescript": "^4.4.4"
|
||||
"release-it": "^14.11.8",
|
||||
"typescript": "^4.5.2"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
## v4.0.0
|
||||
## v4.0.0 - 24 Nov, 2021
|
||||
|
||||
This version is a major change and refactor, adding modern JavaScript capabilities and multiple breaking changes. See the [migration guide](../../docs/v3-to-v4.md) for tips on how to upgrade.
|
||||
|
||||
@@ -17,10 +17,10 @@ This version is a major change and refactor, adding modern JavaScript capabiliti
|
||||
|
||||
- Added support for Promises
|
||||
- Added built-in TypeScript declaration files enabling code completion
|
||||
- Added support for [clustering](../../.github/README.md#cluster)
|
||||
- Added idiomatic arguments and responses to [Redis commands](../../.github/README.md#redis-commands)
|
||||
- Added full support for [Lua Scripts](../../.github/README.md#lua-scripts)
|
||||
- Added support for [SCAN iterators](../../.github/README.md#scan-iterator)
|
||||
- Added support for [clustering](../../README.md#cluster)
|
||||
- Added idiomatic arguments and responses to [Redis commands](../../README.md#redis-commands)
|
||||
- Added full support for [Lua Scripts](../../README.md#lua-scripts)
|
||||
- Added support for [SCAN iterators](../../README.md#scan-iterator)
|
||||
- Added the ability to extend Node Redis with Redis Module commands
|
||||
|
||||
## v3.1.2
|
||||
|
@@ -1,2 +1,2 @@
|
||||
# @node-redis/client
|
||||
The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo.
|
||||
The source code and documentation for this package are in the main [node-redis](https://github.com/redis/node-redis) repo.
|
||||
|
@@ -1,18 +1,15 @@
|
||||
import * as LinkedList from 'yallist';
|
||||
import { AbortError } from '../errors';
|
||||
import { RedisCommandArguments, RedisCommandRawReply } from '../commands';
|
||||
|
||||
// We need to use 'require', because it's not possible with Typescript to import
|
||||
// classes that are exported as 'module.exports = class`, without esModuleInterop
|
||||
// set to true.
|
||||
const RedisParser = require('redis-parser');
|
||||
|
||||
export interface QueueCommandOptions {
|
||||
asap?: boolean;
|
||||
chainId?: symbol;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
interface CommandWaitingToBeSent extends CommandWaitingForReply {
|
||||
args: RedisCommandArguments;
|
||||
chainId?: symbol;
|
||||
@@ -21,27 +18,44 @@ interface CommandWaitingToBeSent extends CommandWaitingForReply {
|
||||
listener(): void;
|
||||
};
|
||||
}
|
||||
|
||||
interface CommandWaitingForReply {
|
||||
resolve(reply?: unknown): void;
|
||||
reject(err: Error): void;
|
||||
channelsCounter?: number;
|
||||
bufferMode?: boolean;
|
||||
}
|
||||
|
||||
export enum PubSubSubscribeCommands {
|
||||
SUBSCRIBE = 'SUBSCRIBE',
|
||||
PSUBSCRIBE = 'PSUBSCRIBE'
|
||||
}
|
||||
|
||||
export enum PubSubUnsubscribeCommands {
|
||||
UNSUBSCRIBE = 'UNSUBSCRIBE',
|
||||
PUNSUBSCRIBE = 'PUNSUBSCRIBE'
|
||||
}
|
||||
|
||||
export type PubSubListener = (message: string, channel: string) => unknown;
|
||||
type PubSubArgumentTypes = Buffer | string;
|
||||
|
||||
export type PubSubListenersMap = Map<string, Set<PubSubListener>>;
|
||||
export type PubSubListener<
|
||||
BUFFER_MODE extends boolean = false,
|
||||
T = BUFFER_MODE extends true ? Buffer : string
|
||||
> = (message: T, channel: T) => unknown;
|
||||
|
||||
interface PubSubListeners {
|
||||
buffers: Set<PubSubListener<true>>;
|
||||
strings: Set<PubSubListener<false>>;
|
||||
}
|
||||
|
||||
type PubSubListenersMap = Map<string, PubSubListeners>;
|
||||
|
||||
interface PubSubState {
|
||||
subscribing: number;
|
||||
subscribed: number;
|
||||
unsubscribing: number;
|
||||
listeners: {
|
||||
channels: PubSubListenersMap;
|
||||
patterns: PubSubListenersMap;
|
||||
};
|
||||
}
|
||||
|
||||
export default class RedisCommandsQueue {
|
||||
static #flushQueue<T extends CommandWaitingForReply>(queue: LinkedList<T>, err: Error): void {
|
||||
@@ -50,53 +64,64 @@ export default class RedisCommandsQueue {
|
||||
}
|
||||
}
|
||||
|
||||
static #emitPubSubMessage(listeners: Set<PubSubListener>, message: string, channel: string): void {
|
||||
for (const listener of listeners) {
|
||||
static #emitPubSubMessage(listenersMap: PubSubListenersMap, message: Buffer, channel: Buffer, pattern?: Buffer): void {
|
||||
const keyString = (pattern || channel).toString(),
|
||||
listeners = listenersMap.get(keyString)!;
|
||||
for (const listener of listeners.buffers) {
|
||||
listener(message, channel);
|
||||
}
|
||||
|
||||
if (!listeners.strings.size) return;
|
||||
|
||||
const messageString = message.toString(),
|
||||
channelString = pattern ? channel.toString() : keyString;
|
||||
for (const listener of listeners.strings) {
|
||||
listener(messageString, channelString);
|
||||
}
|
||||
}
|
||||
|
||||
readonly #maxLength: number | null | undefined;
|
||||
|
||||
readonly #waitingToBeSent = new LinkedList<CommandWaitingToBeSent>();
|
||||
|
||||
readonly #waitingForReply = new LinkedList<CommandWaitingForReply>();
|
||||
|
||||
readonly #pubSubState = {
|
||||
subscribing: 0,
|
||||
subscribed: 0,
|
||||
unsubscribing: 0
|
||||
};
|
||||
#pubSubState: PubSubState | undefined;
|
||||
|
||||
readonly #pubSubListeners = {
|
||||
channels: <PubSubListenersMap>new Map(),
|
||||
patterns: <PubSubListenersMap>new Map()
|
||||
static readonly #PUB_SUB_MESSAGES = {
|
||||
message: Buffer.from('message'),
|
||||
pMessage: Buffer.from('pmessage'),
|
||||
subscribe: Buffer.from('subscribe'),
|
||||
pSubscribe: Buffer.from('psubscribe'),
|
||||
unsubscribe: Buffer.from('unsunscribe'),
|
||||
pUnsubscribe: Buffer.from('punsubscribe')
|
||||
};
|
||||
|
||||
readonly #parser = new RedisParser({
|
||||
returnReply: (reply: unknown) => {
|
||||
if ((this.#pubSubState.subscribing || this.#pubSubState.subscribed) && Array.isArray(reply)) {
|
||||
switch (reply[0]) {
|
||||
case 'message':
|
||||
return RedisCommandsQueue.#emitPubSubMessage(
|
||||
this.#pubSubListeners.channels.get(reply[1])!,
|
||||
reply[2],
|
||||
reply[1]
|
||||
);
|
||||
|
||||
case 'pmessage':
|
||||
return RedisCommandsQueue.#emitPubSubMessage(
|
||||
this.#pubSubListeners.patterns.get(reply[1])!,
|
||||
reply[3],
|
||||
reply[2]
|
||||
);
|
||||
|
||||
case 'subscribe':
|
||||
case 'psubscribe':
|
||||
if (--this.#waitingForReply.head!.value.channelsCounter! === 0) {
|
||||
this.#shiftWaitingForReply().resolve();
|
||||
}
|
||||
return;
|
||||
if (this.#pubSubState && Array.isArray(reply)) {
|
||||
if (RedisCommandsQueue.#PUB_SUB_MESSAGES.message.equals(reply[0])) {
|
||||
return RedisCommandsQueue.#emitPubSubMessage(
|
||||
this.#pubSubState.listeners.channels,
|
||||
reply[2],
|
||||
reply[1]
|
||||
);
|
||||
} else if (RedisCommandsQueue.#PUB_SUB_MESSAGES.pMessage.equals(reply[0])) {
|
||||
return RedisCommandsQueue.#emitPubSubMessage(
|
||||
this.#pubSubState.listeners.patterns,
|
||||
reply[3],
|
||||
reply[2],
|
||||
reply[1]
|
||||
);
|
||||
} else if (
|
||||
RedisCommandsQueue.#PUB_SUB_MESSAGES.subscribe.equals(reply[0]) ||
|
||||
RedisCommandsQueue.#PUB_SUB_MESSAGES.pSubscribe.equals(reply[0]) ||
|
||||
RedisCommandsQueue.#PUB_SUB_MESSAGES.unsubscribe.equals(reply[0]) ||
|
||||
RedisCommandsQueue.#PUB_SUB_MESSAGES.pUnsubscribe.equals(reply[0])
|
||||
) {
|
||||
if (--this.#waitingForReply.head!.value.channelsCounter! === 0) {
|
||||
this.#shiftWaitingForReply().resolve();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,29 +129,26 @@ export default class RedisCommandsQueue {
|
||||
},
|
||||
returnError: (err: Error) => this.#shiftWaitingForReply().reject(err)
|
||||
});
|
||||
|
||||
#chainInExecution: symbol | undefined;
|
||||
|
||||
constructor(maxLength: number | null | undefined) {
|
||||
this.#maxLength = maxLength;
|
||||
}
|
||||
|
||||
addCommand<T = RedisCommandRawReply>(args: RedisCommandArguments, options?: QueueCommandOptions, bufferMode?: boolean): Promise<T> {
|
||||
if (this.#pubSubState.subscribing || this.#pubSubState.subscribed) {
|
||||
if (this.#pubSubState) {
|
||||
return Promise.reject(new Error('Cannot send commands in PubSub mode'));
|
||||
} else 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());
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const node = new LinkedList.Node<CommandWaitingToBeSent>({
|
||||
args,
|
||||
chainId: options?.chainId,
|
||||
bufferMode,
|
||||
resolve,
|
||||
reject,
|
||||
reject
|
||||
});
|
||||
|
||||
if (options?.signal) {
|
||||
@@ -134,7 +156,6 @@ export default class RedisCommandsQueue {
|
||||
this.#waitingToBeSent.removeNode(node);
|
||||
node.value.reject(new AbortError());
|
||||
};
|
||||
|
||||
node.value.abort = {
|
||||
signal: options.signal,
|
||||
listener
|
||||
@@ -144,7 +165,6 @@ export default class RedisCommandsQueue {
|
||||
once: true
|
||||
});
|
||||
}
|
||||
|
||||
if (options?.asap) {
|
||||
this.#waitingToBeSent.unshiftNode(node);
|
||||
} else {
|
||||
@@ -153,28 +173,63 @@ export default class RedisCommandsQueue {
|
||||
});
|
||||
}
|
||||
|
||||
subscribe(command: PubSubSubscribeCommands, channels: string | Array<string>, listener: PubSubListener): Promise<void> {
|
||||
const channelsToSubscribe: Array<string> = [],
|
||||
listeners = command === PubSubSubscribeCommands.SUBSCRIBE ? this.#pubSubListeners.channels : this.#pubSubListeners.patterns;
|
||||
#initiatePubSubState(): PubSubState {
|
||||
return this.#pubSubState ??= {
|
||||
subscribed: 0,
|
||||
subscribing: 0,
|
||||
unsubscribing: 0,
|
||||
listeners: {
|
||||
channels: new Map(),
|
||||
patterns: new Map()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
subscribe<T extends boolean>(
|
||||
command: PubSubSubscribeCommands,
|
||||
channels: PubSubArgumentTypes | Array<PubSubArgumentTypes>,
|
||||
listener: PubSubListener<T>,
|
||||
bufferMode?: T
|
||||
): Promise<void> {
|
||||
const pubSubState = this.#initiatePubSubState(),
|
||||
channelsToSubscribe: Array<PubSubArgumentTypes> = [],
|
||||
listenersMap = command === PubSubSubscribeCommands.SUBSCRIBE ? pubSubState.listeners.channels : pubSubState.listeners.patterns;
|
||||
for (const channel of (Array.isArray(channels) ? channels : [channels])) {
|
||||
if (listeners.has(channel)) {
|
||||
listeners.get(channel)!.add(listener);
|
||||
continue;
|
||||
const channelString = typeof channel === 'string' ? channel : channel.toString();
|
||||
let listeners = listenersMap.get(channelString);
|
||||
if (!listeners) {
|
||||
listeners = {
|
||||
buffers: new Set(),
|
||||
strings: new Set()
|
||||
};
|
||||
listenersMap.set(channelString, listeners);
|
||||
channelsToSubscribe.push(channel);
|
||||
}
|
||||
|
||||
listeners.set(channel, new Set([listener]));
|
||||
channelsToSubscribe.push(channel);
|
||||
// https://github.com/microsoft/TypeScript/issues/23132
|
||||
(bufferMode ? listeners.buffers : listeners.strings).add(listener as any);
|
||||
}
|
||||
|
||||
if (!channelsToSubscribe.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.#pushPubSubCommand(command, channelsToSubscribe);
|
||||
}
|
||||
|
||||
unsubscribe(command: PubSubUnsubscribeCommands, channels?: string | Array<string>, listener?: PubSubListener): Promise<void> {
|
||||
const listeners = command === PubSubUnsubscribeCommands.UNSUBSCRIBE ? this.#pubSubListeners.channels : this.#pubSubListeners.patterns;
|
||||
unsubscribe<T extends boolean>(
|
||||
command: PubSubUnsubscribeCommands,
|
||||
channels?: string | Array<string>,
|
||||
listener?: PubSubListener<T>,
|
||||
bufferMode?: T
|
||||
): Promise<void> {
|
||||
if (!this.#pubSubState) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const listeners = command === PubSubUnsubscribeCommands.UNSUBSCRIBE ?
|
||||
this.#pubSubState.listeners.channels :
|
||||
this.#pubSubState.listeners.patterns;
|
||||
|
||||
if (!channels) {
|
||||
const size = listeners.size;
|
||||
listeners.clear();
|
||||
@@ -183,13 +238,16 @@ export default class RedisCommandsQueue {
|
||||
|
||||
const channelsToUnsubscribe = [];
|
||||
for (const channel of (Array.isArray(channels) ? channels : [channels])) {
|
||||
const set = listeners.get(channel);
|
||||
if (!set) continue;
|
||||
const sets = listeners.get(channel);
|
||||
if (!sets) continue;
|
||||
|
||||
let shouldUnsubscribe = !listener;
|
||||
let shouldUnsubscribe;
|
||||
if (listener) {
|
||||
set.delete(listener);
|
||||
shouldUnsubscribe = set.size === 0;
|
||||
// https://github.com/microsoft/TypeScript/issues/23132
|
||||
(bufferMode ? sets.buffers : sets.strings).delete(listener as any);
|
||||
shouldUnsubscribe = !sets.buffers.size && !sets.strings.size;
|
||||
} else {
|
||||
shouldUnsubscribe = true;
|
||||
}
|
||||
|
||||
if (shouldUnsubscribe) {
|
||||
@@ -197,19 +255,18 @@ export default class RedisCommandsQueue {
|
||||
listeners.delete(channel);
|
||||
}
|
||||
}
|
||||
|
||||
if (!channelsToUnsubscribe.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.#pushPubSubCommand(command, channelsToUnsubscribe);
|
||||
}
|
||||
|
||||
#pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array<string>): Promise<void> {
|
||||
#pushPubSubCommand(command: PubSubSubscribeCommands | PubSubUnsubscribeCommands, channels: number | Array<PubSubArgumentTypes>): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const isSubscribe = command === PubSubSubscribeCommands.SUBSCRIBE || command === PubSubSubscribeCommands.PSUBSCRIBE,
|
||||
const pubSubState = this.#initiatePubSubState(),
|
||||
isSubscribe = command === PubSubSubscribeCommands.SUBSCRIBE || command === PubSubSubscribeCommands.PSUBSCRIBE,
|
||||
inProgressKey = isSubscribe ? 'subscribing' : 'unsubscribing',
|
||||
commandArgs: Array<string> = [command];
|
||||
commandArgs: Array<PubSubArgumentTypes> = [command];
|
||||
|
||||
let channelsCounter: number;
|
||||
if (typeof channels === 'number') { // unsubscribe only
|
||||
@@ -219,18 +276,26 @@ export default class RedisCommandsQueue {
|
||||
channelsCounter = channels.length;
|
||||
}
|
||||
|
||||
this.#pubSubState[inProgressKey] += channelsCounter;
|
||||
pubSubState[inProgressKey] += channelsCounter;
|
||||
|
||||
this.#waitingToBeSent.push({
|
||||
args: commandArgs,
|
||||
channelsCounter,
|
||||
bufferMode: true,
|
||||
resolve: () => {
|
||||
this.#pubSubState[inProgressKey] -= channelsCounter;
|
||||
this.#pubSubState.subscribed += channelsCounter * (isSubscribe ? 1 : -1);
|
||||
pubSubState[inProgressKey] -= channelsCounter;
|
||||
if (isSubscribe) {
|
||||
pubSubState.subscribed += channelsCounter;
|
||||
} else {
|
||||
pubSubState.subscribed -= channelsCounter;
|
||||
if (!pubSubState.subscribed && !pubSubState.subscribing && !pubSubState.subscribed) {
|
||||
this.#pubSubState = undefined;
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
reject: () => {
|
||||
this.#pubSubState[inProgressKey] -= channelsCounter;
|
||||
pubSubState[inProgressKey] -= channelsCounter * (isSubscribe ? 1 : -1);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
@@ -238,22 +303,19 @@ export default class RedisCommandsQueue {
|
||||
}
|
||||
|
||||
resubscribe(): Promise<any> | undefined {
|
||||
if (!this.#pubSubState.subscribed && !this.#pubSubState.subscribing) {
|
||||
if (!this.#pubSubState) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#pubSubState.subscribed = this.#pubSubState.subscribing = 0;
|
||||
|
||||
// TODO: acl error on one channel/pattern will reject the whole command
|
||||
return Promise.all([
|
||||
this.#pushPubSubCommand(PubSubSubscribeCommands.SUBSCRIBE, [...this.#pubSubListeners.channels.keys()]),
|
||||
this.#pushPubSubCommand(PubSubSubscribeCommands.PSUBSCRIBE, [...this.#pubSubListeners.patterns.keys()])
|
||||
this.#pushPubSubCommand(PubSubSubscribeCommands.SUBSCRIBE, [...this.#pubSubState.listeners.channels.keys()]),
|
||||
this.#pushPubSubCommand(PubSubSubscribeCommands.PSUBSCRIBE, [...this.#pubSubState.listeners.patterns.keys()])
|
||||
]);
|
||||
}
|
||||
|
||||
getCommandToSend(): RedisCommandArguments | undefined {
|
||||
const toSend = this.#waitingToBeSent.shift();
|
||||
|
||||
if (toSend) {
|
||||
this.#waitingForReply.push({
|
||||
resolve: toSend.resolve,
|
||||
@@ -262,14 +324,15 @@ export default class RedisCommandsQueue {
|
||||
bufferMode: toSend.bufferMode
|
||||
});
|
||||
}
|
||||
|
||||
this.#chainInExecution = toSend?.chainId;
|
||||
|
||||
return toSend?.args;
|
||||
}
|
||||
|
||||
parseResponse(data: Buffer): void {
|
||||
this.#parser.setReturnBuffers(!!this.#waitingForReply.head?.value.bufferMode);
|
||||
this.#parser.setReturnBuffers(
|
||||
!!this.#waitingForReply.head?.value.bufferMode ||
|
||||
!!this.#pubSubState?.subscribed
|
||||
);
|
||||
this.#parser.execute(data);
|
||||
}
|
||||
|
||||
@@ -277,24 +340,18 @@ export default class RedisCommandsQueue {
|
||||
if (!this.#waitingForReply.length) {
|
||||
throw new Error('Got an unexpected reply from Redis');
|
||||
}
|
||||
|
||||
return this.#waitingForReply.shift()!;
|
||||
}
|
||||
|
||||
flushWaitingForReply(err: Error): void {
|
||||
RedisCommandsQueue.#flushQueue(this.#waitingForReply, err);
|
||||
|
||||
if (!this.#chainInExecution) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (this.#waitingToBeSent.head?.value.chainId === this.#chainInExecution) {
|
||||
this.#waitingToBeSent.shift();
|
||||
}
|
||||
|
||||
this.#chainInExecution = undefined;
|
||||
}
|
||||
|
||||
flushAll(err: Error): void {
|
||||
RedisCommandsQueue.#flushQueue(this.#waitingForReply, err);
|
||||
RedisCommandsQueue.#flushQueue(this.#waitingToBeSent, err);
|
||||
|
@@ -561,63 +561,66 @@ describe('Client', () => {
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
|
||||
testUtils.testWithClient('PubSub', async publisher => {
|
||||
function assertStringListener(message: string, channel: string) {
|
||||
assert.ok(typeof message === 'string');
|
||||
assert.ok(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(),
|
||||
channelListener2 = spy(),
|
||||
patternListener = spy();
|
||||
const channelListener1 = spy(assertBufferListener),
|
||||
channelListener2 = spy(assertStringListener),
|
||||
patternListener = spy(assertStringListener);
|
||||
|
||||
await Promise.all([
|
||||
subscriber.subscribe('channel', channelListener1),
|
||||
subscriber.subscribe('channel', channelListener1, true),
|
||||
subscriber.subscribe('channel', channelListener2),
|
||||
subscriber.pSubscribe('channel*', patternListener)
|
||||
]);
|
||||
|
||||
await Promise.all([
|
||||
waitTillBeenCalled(channelListener1),
|
||||
waitTillBeenCalled(channelListener2),
|
||||
waitTillBeenCalled(patternListener),
|
||||
publisher.publish('channel', 'message')
|
||||
publisher.publish(Buffer.from('channel'), Buffer.from('message'))
|
||||
]);
|
||||
|
||||
assert.ok(channelListener1.calledOnceWithExactly('message', 'channel'));
|
||||
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);
|
||||
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 {
|
||||
|
@@ -388,42 +388,93 @@ export default class RedisClient<M extends RedisModules, S extends RedisScripts>
|
||||
|
||||
select = this.SELECT;
|
||||
|
||||
SUBSCRIBE(channels: string | Array<string>, listener: PubSubListener): Promise<void> {
|
||||
return this.#subscribe(PubSubSubscribeCommands.SUBSCRIBE, channels, listener);
|
||||
#subscribe<T extends boolean>(
|
||||
command: PubSubSubscribeCommands,
|
||||
channels: string | Array<string>,
|
||||
listener: PubSubListener<T>,
|
||||
bufferMode?: T
|
||||
): Promise<void> {
|
||||
const promise = this.#queue.subscribe(
|
||||
command,
|
||||
channels,
|
||||
listener,
|
||||
bufferMode
|
||||
);
|
||||
this.#tick();
|
||||
return promise;
|
||||
}
|
||||
|
||||
SUBSCRIBE<T extends boolean = false>(
|
||||
channels: string | Array<string>,
|
||||
listener: PubSubListener<T>,
|
||||
bufferMode?: T
|
||||
): Promise<void> {
|
||||
return this.#subscribe(
|
||||
PubSubSubscribeCommands.SUBSCRIBE,
|
||||
channels,
|
||||
listener,
|
||||
bufferMode
|
||||
);
|
||||
}
|
||||
|
||||
subscribe = this.SUBSCRIBE;
|
||||
|
||||
PSUBSCRIBE(patterns: string | Array<string>, listener: PubSubListener): Promise<void> {
|
||||
return this.#subscribe(PubSubSubscribeCommands.PSUBSCRIBE, patterns, listener);
|
||||
PSUBSCRIBE<T extends boolean = false>(
|
||||
patterns: string | Array<string>,
|
||||
listener: PubSubListener<T>,
|
||||
bufferMode?: T
|
||||
): Promise<void> {
|
||||
return this.#subscribe(
|
||||
PubSubSubscribeCommands.PSUBSCRIBE,
|
||||
patterns,
|
||||
listener,
|
||||
bufferMode
|
||||
);
|
||||
}
|
||||
|
||||
pSubscribe = this.PSUBSCRIBE;
|
||||
|
||||
#subscribe(command: PubSubSubscribeCommands, channels: string | Array<string>, listener: PubSubListener): Promise<void> {
|
||||
const promise = this.#queue.subscribe(command, channels, listener);
|
||||
#unsubscribe<T extends boolean>(
|
||||
command: PubSubUnsubscribeCommands,
|
||||
channels?: string | Array<string>,
|
||||
listener?: PubSubListener<T>,
|
||||
bufferMode?: T
|
||||
): Promise<void> {
|
||||
const promise = this.#queue.unsubscribe(command, channels, listener, bufferMode);
|
||||
this.#tick();
|
||||
return promise;
|
||||
}
|
||||
|
||||
UNSUBSCRIBE(channels?: string | Array<string>, listener?: PubSubListener): Promise<void> {
|
||||
return this.#unsubscribe(PubSubUnsubscribeCommands.UNSUBSCRIBE, channels, listener);
|
||||
UNSUBSCRIBE<T extends boolean = false>(
|
||||
channels?: string | Array<string>,
|
||||
listener?: PubSubListener<T>,
|
||||
bufferMode?: T
|
||||
): Promise<void> {
|
||||
return this.#unsubscribe(
|
||||
PubSubUnsubscribeCommands.UNSUBSCRIBE,
|
||||
channels,
|
||||
listener,
|
||||
bufferMode
|
||||
);
|
||||
}
|
||||
|
||||
unsubscribe = this.UNSUBSCRIBE;
|
||||
|
||||
PUNSUBSCRIBE(patterns?: string | Array<string>, listener?: PubSubListener): Promise<void> {
|
||||
return this.#unsubscribe(PubSubUnsubscribeCommands.PUNSUBSCRIBE, patterns, listener);
|
||||
PUNSUBSCRIBE<T extends boolean = false>(
|
||||
patterns?: string | Array<string>,
|
||||
listener?: PubSubListener<T>,
|
||||
bufferMode?: T
|
||||
): Promise<void> {
|
||||
return this.#unsubscribe(
|
||||
PubSubUnsubscribeCommands.PUNSUBSCRIBE,
|
||||
patterns,
|
||||
listener,
|
||||
bufferMode
|
||||
);
|
||||
}
|
||||
|
||||
pUnsubscribe = this.PUNSUBSCRIBE;
|
||||
|
||||
#unsubscribe(command: PubSubUnsubscribeCommands, channels?: string | Array<string>, listener?: PubSubListener): Promise<void> {
|
||||
const promise = this.#queue.unsubscribe(command, channels, listener);
|
||||
this.#tick();
|
||||
return promise;
|
||||
}
|
||||
|
||||
QUIT(): Promise<void> {
|
||||
return this.#socket.quit(() => {
|
||||
const quitPromise = this.#queue.addCommand(['QUIT']);
|
||||
|
@@ -42,20 +42,8 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisSc
|
||||
throw new Error('None of the root nodes is available');
|
||||
}
|
||||
|
||||
async discover(startWith: RedisClientType<M, S>): Promise<void> {
|
||||
if (await this.#discoverNodes(startWith.options)) return;
|
||||
|
||||
for (const { client } of this.#nodeByUrl.values()) {
|
||||
if (client === startWith) continue;
|
||||
|
||||
if (await this.#discoverNodes(client.options)) return;
|
||||
}
|
||||
|
||||
throw new Error('None of the cluster nodes is available');
|
||||
}
|
||||
|
||||
async #discoverNodes(clientOptions?: RedisClusterClientOptions): Promise<boolean> {
|
||||
const client = new this.#Client(clientOptions);
|
||||
const client = this.#initiateClient(clientOptions);
|
||||
|
||||
await client.connect();
|
||||
|
||||
@@ -72,6 +60,29 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisSc
|
||||
}
|
||||
}
|
||||
|
||||
#runningRediscoverPromise?: Promise<void>;
|
||||
|
||||
async rediscover(startWith: RedisClientType<M, S>): Promise<void> {
|
||||
if (!this.#runningRediscoverPromise) {
|
||||
this.#runningRediscoverPromise = this.#rediscover(startWith)
|
||||
.finally(() => this.#runningRediscoverPromise = undefined);
|
||||
}
|
||||
|
||||
return this.#runningRediscoverPromise;
|
||||
}
|
||||
|
||||
async #rediscover(startWith: RedisClientType<M, S>): Promise<void> {
|
||||
if (await this.#discoverNodes(startWith.options)) return;
|
||||
|
||||
for (const { client } of this.#nodeByUrl.values()) {
|
||||
if (client === startWith) continue;
|
||||
|
||||
if (await this.#discoverNodes(client.options)) return;
|
||||
}
|
||||
|
||||
throw new Error('None of the cluster nodes is available');
|
||||
}
|
||||
|
||||
async #reset(masters: Array<RedisClusterMasterNode>): Promise<void> {
|
||||
// Override this.#slots and add not existing clients to this.#nodeByUrl
|
||||
const promises: Array<Promise<void>> = [],
|
||||
@@ -103,18 +114,23 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisSc
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
#clientOptionsDefaults(options: RedisClusterClientOptions): RedisClusterClientOptions {
|
||||
#clientOptionsDefaults(options?: RedisClusterClientOptions): RedisClusterClientOptions | undefined {
|
||||
if (!this.#options.defaults) return options;
|
||||
|
||||
const merged = Object.assign({}, this.#options.defaults, options);
|
||||
|
||||
if (options.socket && this.#options.defaults.socket) {
|
||||
if (options?.socket && this.#options.defaults.socket) {
|
||||
Object.assign({}, this.#options.defaults.socket, options.socket);
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
#initiateClient(options?: RedisClusterClientOptions): RedisClientType<M, S> {
|
||||
return new this.#Client(this.#clientOptionsDefaults(options))
|
||||
.on('error', this.#onError);
|
||||
}
|
||||
|
||||
#initiateClientForNode(nodeData: RedisClusterMasterNode | RedisClusterReplicaNode, readonly: boolean, clientsInUse: Set<string>, promises: Array<Promise<void>>): ClusterNode<M, S> {
|
||||
const url = `${nodeData.host}:${nodeData.port}`;
|
||||
clientsInUse.add(url);
|
||||
@@ -123,15 +139,13 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisSc
|
||||
if (!node) {
|
||||
node = {
|
||||
id: nodeData.id,
|
||||
client: new this.#Client(
|
||||
this.#clientOptionsDefaults({
|
||||
socket: {
|
||||
host: nodeData.host,
|
||||
port: nodeData.port
|
||||
},
|
||||
readonly
|
||||
})
|
||||
)
|
||||
client: this.#initiateClient({
|
||||
socket: {
|
||||
host: nodeData.host,
|
||||
port: nodeData.port
|
||||
},
|
||||
readonly
|
||||
})
|
||||
};
|
||||
promises.push(node.client.connect());
|
||||
this.#nodeByUrl.set(url, node);
|
||||
|
@@ -157,7 +157,7 @@ export default class RedisCluster<M extends RedisModules = Record<string, never>
|
||||
const url = err.message.substring(err.message.lastIndexOf(' ') + 1);
|
||||
let node = this.#slots.getNodeByUrl(url);
|
||||
if (!node) {
|
||||
await this.#slots.discover(client);
|
||||
await this.#slots.rediscover(client);
|
||||
node = this.#slots.getNodeByUrl(url);
|
||||
|
||||
if (!node) {
|
||||
@@ -168,7 +168,7 @@ export default class RedisCluster<M extends RedisModules = Record<string, never>
|
||||
await node.client.asking();
|
||||
return node.client;
|
||||
} else if (err.message.startsWith('MOVED')) {
|
||||
await this.#slots.discover(client);
|
||||
await this.#slots.rediscover(client);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -48,6 +48,31 @@ describe('CLUSTER NODES', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should support urls without cport', () => {
|
||||
assert.deepEqual(
|
||||
transformReply(
|
||||
'id 127.0.0.1:30001 master - 0 0 0 connected 0-16384\n'
|
||||
),
|
||||
[{
|
||||
id: 'id',
|
||||
url: '127.0.0.1:30001',
|
||||
host: '127.0.0.1',
|
||||
port: 30001,
|
||||
cport: null,
|
||||
flags: ['master'],
|
||||
pingSent: 0,
|
||||
pongRecv: 0,
|
||||
configEpoch: 0,
|
||||
linkState: RedisClusterNodeLinkStates.CONNECTED,
|
||||
slots: [{
|
||||
from: 0,
|
||||
to: 16384
|
||||
}],
|
||||
replicas: []
|
||||
}]
|
||||
);
|
||||
});
|
||||
|
||||
it.skip('with importing slots', () => {
|
||||
assert.deepEqual(
|
||||
transformReply(
|
||||
|
@@ -10,7 +10,7 @@ export enum RedisClusterNodeLinkStates {
|
||||
interface RedisClusterNodeTransformedUrl {
|
||||
host: string;
|
||||
port: number;
|
||||
cport: number;
|
||||
cport: number | null;
|
||||
}
|
||||
|
||||
export interface RedisClusterReplicaNode extends RedisClusterNodeTransformedUrl {
|
||||
@@ -86,7 +86,16 @@ export function transformReply(reply: string): Array<RedisClusterMasterNode> {
|
||||
|
||||
function transformNodeUrl(url: string): RedisClusterNodeTransformedUrl {
|
||||
const indexOfColon = url.indexOf(':'),
|
||||
indexOfAt = url.indexOf('@', indexOfColon);
|
||||
indexOfAt = url.indexOf('@', indexOfColon),
|
||||
host = url.substring(0, indexOfColon);
|
||||
|
||||
if (indexOfAt === -1) {
|
||||
return {
|
||||
host,
|
||||
port: Number(url.substring(indexOfColon + 1)),
|
||||
cport: null
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
host: url.substring(0, indexOfColon),
|
||||
|
@@ -1,26 +1,36 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { transformArguments } from './LINDEX';
|
||||
|
||||
describe('LINDEX', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('key', 'element'),
|
||||
['LINDEX', 'key', 'element']
|
||||
transformArguments('key', 0),
|
||||
['LINDEX', 'key', '0']
|
||||
);
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.lIndex', async client => {
|
||||
assert.equal(
|
||||
await client.lIndex('key', 'element'),
|
||||
null
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
describe('client.lIndex', () => {
|
||||
testUtils.testWithClient('null', async client => {
|
||||
assert.equal(
|
||||
await client.lIndex('key', 0),
|
||||
null
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
|
||||
testUtils.testWithClient('with value', async client => {
|
||||
const [, lIndexReply] = await Promise.all([
|
||||
client.lPush('key', 'element'),
|
||||
client.lIndex('key', 0)
|
||||
]);
|
||||
|
||||
assert.equal(lIndexReply, 'element');
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
||||
|
||||
testUtils.testWithCluster('cluster.lIndex', async cluster => {
|
||||
assert.equal(
|
||||
await cluster.lIndex('key', 'element'),
|
||||
await cluster.lIndex('key', 0),
|
||||
null
|
||||
);
|
||||
}, GLOBAL.CLUSTERS.OPEN);
|
||||
});
|
||||
});
|
@@ -1,9 +1,8 @@
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(key: string, element: string): Array<string> {
|
||||
return ['LINDEX', key, element];
|
||||
export function transformArguments(key: string, index: number): Array<string> {
|
||||
return ['LINDEX', key, index.toString()];
|
||||
}
|
||||
|
||||
export declare function transformReply(): string | null;
|
||||
export declare function transformReply(): string | null;
|
@@ -1,4 +1,6 @@
|
||||
export function transformArguments(channel: string, message: string): Array<string> {
|
||||
import { RedisCommandArguments } from '.';
|
||||
|
||||
export function transformArguments(channel: string | Buffer, message: string | Buffer): RedisCommandArguments {
|
||||
return ['PUBLISH', channel, message];
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-redis/client",
|
||||
"version": "1.0.0-rc.0",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -19,22 +19,22 @@
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@node-redis/test-utils": "*",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/redis-parser": "^3.0.0",
|
||||
"@types/sinon": "^10.0.6",
|
||||
"@types/yallist": "^4.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint": "^8.2.0",
|
||||
"eslint": "^8.3.0",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.7",
|
||||
"release-it": "^14.11.8",
|
||||
"sinon": "^12.0.1",
|
||||
"source-map-support": "^0.5.20",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typedoc": "^0.22.9",
|
||||
"typedoc": "^0.22.10",
|
||||
"typedoc-github-wiki-theme": "^0.6.0",
|
||||
"typedoc-plugin-markdown": "^3.11.6",
|
||||
"typescript": "^4.4.4"
|
||||
"typedoc-plugin-markdown": "^3.11.7",
|
||||
"typescript": "^4.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
|
@@ -1,2 +1,80 @@
|
||||
# @node-redis/json
|
||||
The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo.
|
||||
|
||||
This package provides support for the [RedisJSON](https://redisjson.io) module, which adds JSON as a native data type to Redis. It extends the [Node Redis client](https://github.com/redis/node-redis) to include functions for each of the RedisJSON commands.
|
||||
|
||||
To use these extra commands, your Redis server must have the RedisJSON module installed.
|
||||
|
||||
## Usage
|
||||
|
||||
For a complete example, see [`managing-json.js`](https://github.com/redis/node-redis/blob/master/examples/managing-json.js) in the Node Redis examples folder.
|
||||
|
||||
### Storing JSON Documents in Redis
|
||||
|
||||
The [`JSON.SET`](https://oss.redis.com/redisjson/commands/#jsonset) command stores a JSON value at a given JSON Path in a Redis key.
|
||||
|
||||
Here, we'll store a JSON document in the root of the Redis key "`mydoc`":
|
||||
|
||||
```javascript
|
||||
import { createClient } from 'redis';
|
||||
|
||||
...
|
||||
await client.json.set('noderedis:jsondata', '$', {
|
||||
name: 'Roberta McDonald',
|
||||
pets: [
|
||||
{
|
||||
name: 'Rex',
|
||||
species: 'dog',
|
||||
age: 3,
|
||||
isMammal: true
|
||||
},
|
||||
{
|
||||
name: 'Goldie',
|
||||
species: 'fish',
|
||||
age: 2,
|
||||
isMammal: false
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
For more information about RedisJSON's path syntax, [check out the documentation](https://oss.redis.com/redisjson/path/).
|
||||
|
||||
### Retrieving JSON Documents from Redis
|
||||
|
||||
With RedisJSON, we can retrieve all or part(s) of a JSON document using the [`JSON.GET`]() command and one or more JSON Paths. Let's get the name and age of one of the pets:
|
||||
|
||||
```javascript
|
||||
const results = await client.json.get('noderedis:jsondata', {
|
||||
path: [
|
||||
'.pets[1].name',
|
||||
'.pets[1].age'
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
`results` will contain the following:
|
||||
|
||||
```javascript
|
||||
{ '.pets[1].name': 'Goldie', '.pets[1].age': 2 }
|
||||
```
|
||||
|
||||
### Performing Atomic Updates on JSON Documents Stored in Redis
|
||||
|
||||
RedisJSON includes commands that can atomically update values in a JSON document, in place in Redis without having to first retrieve the entire document.
|
||||
|
||||
Using the [`JSON.NUMINCRBY`](https://oss.redis.com/redisjson/commands/#jsonnumincrby) command, we can update the age of one of the pets like this:
|
||||
|
||||
```javascript
|
||||
await client.json.numIncrBy('noderedis:jsondata', '.pets[1].age', 1);
|
||||
```
|
||||
|
||||
And we can add a new object to the pets array with the [`JSON.ARRAPPEND`](https://oss.redis.com/redisjson/commands/#jsonarrappend) command:
|
||||
|
||||
```javascript
|
||||
await client.json.arrAppend('noderedis:jsondata', '.pets', {
|
||||
name: 'Robin',
|
||||
species: 'bird',
|
||||
age: 1,
|
||||
isMammal: false
|
||||
});
|
||||
```
|
||||
|
@@ -14,4 +14,4 @@ export function transformArguments(key: string, path?: string, index?: number):
|
||||
return args;
|
||||
}
|
||||
|
||||
export { transformRedisJsonNullArrayReply as transformReply } from '.';
|
||||
export { transformRedisJsonNullArrayNullReply as transformReply } from '.';
|
||||
|
@@ -84,8 +84,9 @@ export function transformRedisJsonNullReply(json: string | null): RedisJSON | nu
|
||||
return transformRedisJsonReply(json);
|
||||
}
|
||||
|
||||
export function transformRedisJsonNullArrayNullReply(jsons: Array<string | null> | null): Array<RedisJSON | null> | null {
|
||||
if (jsons === null) return null;
|
||||
|
||||
export function transformRedisJsonNullArrayReply(jsons: Array<string | null>): Array<RedisJSON | null> {
|
||||
return jsons.map(transformRedisJsonNullReply);
|
||||
}
|
||||
|
||||
|
@@ -9,16 +9,16 @@
|
||||
"build": "tsc"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@node-redis/client": "^1.0.0-rc"
|
||||
"@node-redis/client": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@node-redis/test-utils": "*",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/node": "^16.11.10",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.7",
|
||||
"source-map-support": "^0.5.20",
|
||||
"release-it": "^14.11.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.4.4"
|
||||
"typescript": "^4.5.2"
|
||||
}
|
||||
}
|
||||
|
@@ -1,2 +1,120 @@
|
||||
# @node-redis/search
|
||||
The sources and docs for this package are in the main [node-redis](https://github.com/redis/node-redis) repo.
|
||||
|
||||
This package provides support for the [RediSearch](https://redisearch.io) module, which adds indexing and querying support for data stored in Redis Hashes or as JSON documents with the RedisJSON module. It extends the [Node Redis client](https://github.com/redis/node-redis) to include functions for each of the RediSearch commands.
|
||||
|
||||
To use these extra commands, your Redis server must have the RediSearch module installed. To index and query JSON documents, you'll also need to add the RedisJSON module.
|
||||
|
||||
## Usage
|
||||
|
||||
For complete examples, see [`search-hashes.js`](https://github.com/redis/node-redis/blob/master/examples/search-hashes.js) and [`search-json.js`](https://github.com/redis/node-redis/blob/master/examples/search-json.js) in the Node Redis examples folder.
|
||||
|
||||
### Indexing and Querying Data in Redis Hashes
|
||||
|
||||
#### Creating an Index
|
||||
|
||||
Before we can perform any searches, we need to tell RediSearch how to index our data, and which Redis keys to find that data in. The [FT.CREATE](https://oss.redis.com/redisearch/Commands/#ftcreate) command creates a RediSearch index. Here's how to use it to create an index we'll call `idx:animals` where we want to index hashes containing `name`, `species` and `age` fields, and whose key names in Redis begin with the prefix `noderedis:animals`:
|
||||
|
||||
```javascript
|
||||
await client.ft.create('idx:animals', {
|
||||
name: {
|
||||
type: SchemaFieldTypes.TEXT,
|
||||
sortable: true
|
||||
},
|
||||
species: SchemaFieldTypes.TAG,
|
||||
age: SchemaFieldTypes.NUMERIC
|
||||
}, {
|
||||
ON: 'HASH',
|
||||
PREFIX: 'noderedis:animals'
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
See the [`FT.CREATE` documentation](https://oss.redis.com/redisearch/Commands/#ftcreate) for information about the different field types and additional options.
|
||||
|
||||
#### Querying the Index
|
||||
|
||||
Once we've created an index, and added some data to Redis hashes whose keys begin with the prefix `noderedis:animals`, we can start writing some search queries. RediSearch supports a rich query syntax for full-text search, faceted search, aggregation and more. Check out the [`FT.SEARCH` documentation](https://oss.redis.com/redisearch/Commands/#ftsearch) and the [query syntax reference](https://oss.redis.com/redisearch/Query_Syntax/) for more information.
|
||||
|
||||
Let's write a query to find all the animals where the `species` field has the value `dog`:
|
||||
|
||||
```javascript
|
||||
const results = await client.ft.search('idx:animals', '@species:{dog}');
|
||||
```
|
||||
|
||||
`results` looks like this:
|
||||
|
||||
```javascript
|
||||
{
|
||||
total: 2,
|
||||
documents: [
|
||||
{
|
||||
id: 'noderedis:animals:4',
|
||||
value: {
|
||||
name: 'Fido',
|
||||
species: 'dog',
|
||||
age: '7'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'noderedis:animals:3',
|
||||
value: {
|
||||
name: 'Rover',
|
||||
species: 'dog',
|
||||
age: '9'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Indexing and Querying Data with RedisJSON
|
||||
|
||||
RediSearch can also index and query JSON documents stored in Redis using the RedisJSON module. The approach is similar to that for indexing and searching data in hashes, but we can now use JSON Path like syntax and the data no longer has to be flat name/value pairs - it can contain nested objects and arrays.
|
||||
|
||||
#### Creating an Index
|
||||
|
||||
As before, we create an index with the `FT.CREATE` command, this time specifying we want to index JSON documents that look like this:
|
||||
|
||||
```javascript
|
||||
{
|
||||
name: 'Alice',
|
||||
age: 32,
|
||||
coins: 100
|
||||
}
|
||||
```
|
||||
|
||||
Each document represents a user in some system, and users have name, age and coins properties.
|
||||
|
||||
One way we might choose to index these documents is as follows:
|
||||
|
||||
```javascript
|
||||
await client.ft.create('idx:users', {
|
||||
'$.name': {
|
||||
type: SchemaFieldTypes.TEXT,
|
||||
SORTABLE: 'UNF'
|
||||
},
|
||||
'$.age': {
|
||||
type: SchemaFieldTypes.NUMERIC,
|
||||
AS: 'age'
|
||||
},
|
||||
'$.coins': {
|
||||
type: SchemaFieldTypes.NUMERIC,
|
||||
AS: 'coins'
|
||||
}
|
||||
}, {
|
||||
ON: 'JSON',
|
||||
PREFIX: 'noderedis:users'
|
||||
});
|
||||
```
|
||||
|
||||
Note that we're using JSON Path to specify where the fields to index are in our JSON documents, and the `AS` clause to define a name/alias for each field. We'll use these when writing queries.
|
||||
|
||||
#### Querying the Index
|
||||
|
||||
Now we have an index and some data stored as JSON documents in Redis (see the [JSON package documentation](https://github.com/redis/node-redis/tree/master/packages/json) for examples of how to store JSON), we can write some queries...
|
||||
|
||||
We'll use the [RediSearch query language](https://oss.redis.com/redisearch/Query_Syntax/) and [`FT.SEARCH`](https://oss.redis.com/redisearch/Commands/#ftsearch) command. Here's a query to find users under the age of 30:
|
||||
|
||||
```javascript
|
||||
await client.ft.search('idx:users', '@age:[0 30]');
|
||||
```
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-redis/search",
|
||||
"version": "1.0.0-rc.0",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -9,16 +9,16 @@
|
||||
"build": "tsc"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@node-redis/client": "^1.0.0-rc"
|
||||
"@node-redis/client": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@node-redis/test-utils": "*",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/node": "^16.11.10",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.7",
|
||||
"source-map-support": "^0.5.20",
|
||||
"release-it": "^14.11.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.4.4"
|
||||
"typescript": "^4.5.2"
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo testststealkshdfklhasdf
|
||||
|
||||
echo $REDIS_ARGUMENTS
|
||||
|
||||
redis-server $REDIS_ARGUMENTS
|
||||
|
@@ -8,19 +8,19 @@
|
||||
"test": "echo \"TODO\""
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@node-redis/client": "^1.0.0-rc"
|
||||
"@node-redis/client": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^16.11.7",
|
||||
"@types/yargs": "^17.0.5",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/yargs": "^17.0.7",
|
||||
"mocha": "^9.1.3",
|
||||
"nyc": "^15.1.0",
|
||||
"release-it": "^14.11.7",
|
||||
"source-map-support": "^0.5.20",
|
||||
"release-it": "^14.11.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.4.4",
|
||||
"typescript": "^4.5.2",
|
||||
"yargs": "^17.2.1"
|
||||
}
|
||||
}
|
||||
|
6
packages/time-series/.npmignore
Normal file
6
packages/time-series/.npmignore
Normal file
@@ -0,0 +1,6 @@
|
||||
.nyc_output/
|
||||
coverage/
|
||||
lib/
|
||||
.nycrc.json
|
||||
.release-it.json
|
||||
tsconfig.json
|
Reference in New Issue
Block a user